我可以在打字稿中检查联合类型的类型吗?

mjo*_*ngr 6 typescript

有没有办法对一个对象的"instanceof"式查询对照语言中内置的联合类型?

我有一个带有union类型的类型别名,如下所示:

type MyType = Foo | Bar | Thing;
Run Code Online (Sandbox Code Playgroud)

每一个Foo,BarThing继承自Base

class Base { /* ... */ }
class Foo extends Base { /* ... */ }
class Bar extends Base { /* ... */ }
class Thing extends Base { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

一些方法返回一个Base.

function getBase(): Base { /* ... */ return base; }
Run Code Online (Sandbox Code Playgroud)

理想情况下,我想创建另一个可以返回MyType后调用的方法getBase()

function getMyType(): MyType { 
    var item = getBase();
    if (item instanceof MyType)
        return item;
    else
        return null;
}
Run Code Online (Sandbox Code Playgroud)

如果MyType不是类型别名,则上述代码将起作用.但是,由于它是一个类型别名,它似乎不起作用.因此,为了重新思考我的问题,这种语言内置了什么?

显然,我想要的是通过针对每个单独的类检查instanceof查询来完成:

function getMyType(): MyType { 
    var item = getBase();
    if (item instanceof Foo || item instanceof Bar || item instanceof Thing)
        return item;
    else
        return null;
}
Run Code Online (Sandbox Code Playgroud)

但这并不理想; 如果一些未来的开发人员想要创建OtherThing和扩展MyType以包含这个新类,那么希望记得更新getMyType().

语言中是否有内置功能来解决这个问题,或者是否有更好的方法可以做到这一点?

Exp*_*ter 60

如果您知道感兴趣的类型的某些特定键,您可以通过运算符来完成in

function move(pet: Fish | Bird) {
  if ("swim" in pet) {
    return pet.swim(); //pet is Fish
  }
  return pet.fly(); //pet is Bird
}
Run Code Online (Sandbox Code Playgroud)

对我来说比附加功能要容易得多。这就是我一直在寻找的。

文档。

  • 注意:编译为 js 后,如果属性名称被缩小,则 `in` 运算符将被破坏。令人讨厌的“Heisenbug”——代码在调试模式下运行良好。 (9认同)
  • @PeterDavis 你会如何做才能证明这是“heisenbug”? (2认同)
  • @PeterDavis 我觉得这有点难以置信,因为 [in](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#in) 运算符确实来自本机 JS!TypeScript 只是添加了类型保护。 (2认同)

jts*_*ven 11

自已接受的答案发布以来,Typescript 引入了一些新功能,使这一切变得更容易。从 Typescript 3.8.3 开始,我将在 2020 年执行此操作:

const myTypes = ['Foo', 'Bar', 'Thing'] as const;
type MyType = typeof myType[number]; // 'Foo' | 'Bar' | 'Thing'

// Return a typed MyType if string is valid (else throw).
function getMyType(maybeMyType: string): MyType {
    const myType = myTypes.find((validType) => validType === maybeMyType);
    if (myType) {
        return myType;
    }
    throw new Error(`String "${maybeMyType}" is not of type MyType.`);
}

// Use it like this:
const definitelyMyType = getMyType('Foo');
Run Code Online (Sandbox Code Playgroud)

或者,如果您更喜欢不太安全但可以说更易读的自定义类型保护样式,您可以这样做:

// Define a custom type guard to validate & assert that maybeMyType is MyType.
function isMyType(maybeMyType: string): maybeMyType is MyType {
    return myTypes.includes(maybeMyType);
}

// Use it like this:
const maybeMyType = 'Foo';
if (isMyType(maybeMyType)) {
    const definitelyMyType: MyType = maybeMyType;
}
Run Code Online (Sandbox Code Playgroud)

  • @SuryaPratap您可以使用`myTypes.includes`来检查变量是否具有某种类型,但是如果没有类型保护,TS编译器将不会知道它。 (2认同)

Rya*_*ugh 8

没有类型别名的运行时表示,因此本身没有任何“内置”来进行这种检查。

不过,这种模式相当容易维护:

// Future devs: Please keep these in sync
type MyType       =  Foo|Bar|Thing;
let MyTypeClasses = [Foo,Bar,Thing];

function getMyType(): MyType { 
    var item = getBase();
    if (MyTypeClasses.some(c => item instanceof c))
        return item;
    else
        return null;
}
Run Code Online (Sandbox Code Playgroud)

  • 当然不完美,但合理。 (2认同)
  • 这不适合我 - 因为它似乎使用类作为值? (2认同)