export type Animal<T extends Kind> = T extends "cat"
? CatData<T>
: DogData<"dog">
export type Kind = "cat" | "dog"
type CatData<T extends "cat"> = {
kind: T
someData: number[]
}
type DogData<T extends "dog"> = {
kind: T
someData: string[]
}
const fun = <T extends Kind>(kind: T): Animal<T> => {
switch (kind) {
case "cat":
return {
kind,
someData: [33],
}
default:
return {
kind,
someData: ["data"],
}
}
}
对于上面的代码,我在switch语句中得到了两个关于这两种情况的错误。两个错误相同:
test.ts 24 7 error 2322 Type '{ kind: T; someData: string[]; }' is not assignable to type 'Animal<T>'. (lsp)
我不明白为什么这些类型不能分配给动物。我可能遗漏了一些内容,但它们看起来像是函数参数提供的带有T的泛型Animal类型的实例。
上面的代码出了什么问题?
在fun
中,您希望缩小类型T
,从而缩小Animal<T>
,但实际上只是缩小参数kind
的类型。检查kind
等于(例如("cat"
并不一定意味着T
类型的其他变量,包括函数的返回值。这在一定程度上是TypeScript的限制。
这里需要的习惯用法是使用索引访问类型的Kind
到Animal
类型的映射。这将清理类型的结构,但它并没有解决上述限制。函数内部的包装器虽然不是很优雅,但可以解决这个问题。以下是全部内容:
interface KindMap {
"cat": CatData
"dog": DogData
}
export type Kind = keyof KindMap
export type Animal<T extends Kind> = KindMap[T]
type CatData = {
kind: "cat"
someData: number[]
}
type DogData = {
kind: "dog"
someData: string[]
}
const fun = <T extends Kind>(kind: T): Animal<T> => {
return ((): KindMap[Kind] => {
switch (kind) {
case "cat":
return {
kind,
someData: [33],
}
default:
return {
kind,
someData: ["data"],
}
}
}) () as Animal<T>;
}
console.log(fun("cat")) // {"kind":"cat","someData":[33]}
console.log(fun("dog")) // {"kind":"dog","someData":["data"]}
// console.log(fun("sphinx")) // Argument of type '"sphinx"' is not assignable to parameter of type 'keyof KindMap'.(2345)
(TypeScript游乐场链接(