为不同类型提供不同代码块的类型安全方式是什么



我想要if条件取决于参数类型。

在下面的例子中,typescript无法识别如果我通过";猫;作为type参数,则canMeow应在if条件下可用。

有什么类型的安全方法可以实现以下目的吗?

type Cat = {
name: string;
canMeow: boolean;
}
type Dog = {
name: string;
canBark: boolean
}
type TypeMap = {
dog: Dog;
cat: Cat;
}
type Animal<T> = {
type: T;
data: TypeMap[T]
}
function canMakeSound<Type extends keyof TypeMap>(animal: Animal<Type>){
if(animal.type == "cat"){
return animal.data.canMeow; // <--- error
}
else if(animal.type == "dog"){
return animal.data.canBark; // <--- error
}
}
canMakeSound({type: "cat", data: {name: "kitty", canMeow: true}});

TypeScript中目前没有通过控制流分析缩小泛型类型参数的机制。也就是说,如果animal是泛型类型T,那么检查animal.type可能有助于缩小animal.type,但它将而不是缩小或重新约束T本身。因此,对于编译器不知道的Tanimal.data将保持为TypeMap[T]。这里有各种悬而未决的问题需要改进,比如microsoft/TypeScript#33014,但目前它还不是语言的一部分。

因此,与其给animal一个泛型类型,不如考虑编译器通过检查animal.type来缩小animal的类型。。。即受歧视的联盟。以下是如何生成这种类型:

type Animal = { [K in keyof TypeMap]: {
type: K;
data: TypeMap[K]
} }[keyof TypeMap]
/* type Animal = {
type: "dog";
data: Dog;
} | {
type: "cat";
data: Cat;
} */

这里Animal是通过立即索引到映射类型中创建的,这使其成为上面所示的并集类型。type是文字类型"dog"data将是Dog,或者type是文字类型"cat"data将是Cat。这使得type成为Animal判别式属性,如果您检查该属性,它将把整个对象的类型缩小到仅符合检查的联合成员:

function canMakeSound(animal: Animal) {
if (animal.type == "cat") {
return animal.data.canMeow; // okay
}
else if (animal.type == "dog") {
return animal.data.canBark; // okay
}
throw new Error(); // added this due to microsoft/TypeScript/issues/21985
}

这一切都很好,只是我添加了一行throw来安抚编译器;它无法从那些if/else语句中判断出animal.type检查是详尽无遗的,因此它抱怨您从一开始就没有return。这是microsoft/TypeScript#21985上的一个已知问题。

让我们测试一下:

console.log(
canMakeSound({ type: "cat", data: { name: "kitty", canMeow: true } })
); // true
canMakeSound({ type: "dog", data: { name: "kitty", canMeow: true } }); // error!
// ----------------------------------------------> ~~~~~~~~~~~~~
// Type '{ name: string; canMeow: boolean; }' is not assignable to type 'Dog'

看起来不错。它的行为与预期的一样,如果传入不符合正确typedata,编译器会发出警告。

游乐场链接到代码

相关内容

  • 没有找到相关文章

最新更新