首页 > 解决方案 > Type guards to convert interface X into Required?

问题描述

I have an interface Foo with an optional field and want to call a function that takes Required<Foo>. Is there a type-safe way of calling this function? For example

interface FooBar {foo:string, bar?: string}

const myFooBar:FooBar = {foo:'foo'}
if (myFooBar.bar !== undefined) {
    test(myFooBar)
}

function test(fooBar: Required<FooBar>) {
    console.log(fooBar.bar)
}

but the compiler reports

Argument of type 'FooBar' is not assignable to parameter of type 'Required<FooBar>'.
  Types of property 'bar' are incompatible.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.(2345)

I could just myFooBar explicitly but does anyone know of a type-safe of doing this that doesn't involve casting.

Link to playground: https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgGIHt0CE5WQbxkwC4BnMKUAcwBpkAjXAfmOXMpCoF8AoHhdCHLIAtgE8M2XMUk48AXgJF0xAOTLVvYDGQAKcbNwA6RngCE8xQFcQAEwgxQEWwEoCPZJ+SRy+iZjkXHl4eGBsEMGBBbwhfZTlWACUIAEcrYChnAB5DKAA+N3wPLwEhdAAbCCNy9CpdeONTIN4gA

标签: typescript

解决方案


You can use a type guard function for that:

function hasBar(fooBar: FooBar): fooBar is Required<FooBar> {
    return typeof myFooBar.bar !== "undefined";
}

Then

if (hasBar(myFooBar)) {
    test(myFooBar)
}

Playground link

It doesn't have to be so specific to the FooBar type if you don't want it to be. It could be specific to the fact that bar is type string:

function hasBarString<T>(fooBar: T): fooBar is T & {bar: string} {
    return typeof myFooBar.bar === "string";
}

const myFooBar:FooBar = {foo:'foo'}
if (hasBarString(myFooBar)) {
    test(myFooBar)
}

Playground link


推荐阅读