Reputation: 325
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"],
}
}
}
For above code I got two errors regarding both cases in switch statement. Both errors are the same:
test.ts 24 7 error 2322 Type '{ kind: T; someData: string[]; }' is not assignable to type 'Animal<T>'. (lsp)
I don't understand why those types are not assignable to Animal. I'm probably missing something but they look like they are instances of generic Animal type with T provided by function's argument.
What's wrong with above code?
Upvotes: 0
Views: 1278
Reputation: 2099
In your fun
, you want to narrow the type T
, and thus Animal<T>
, but you're actually just narrowing the type of the argument kind
. Checking that kind
equals, e.g., "cat"
doesn't necessarily imply anything about other variables of type T
, including the return value of the function. That is in part a limitation in TypeScript.
The idiom that you want here is a map of Kind
s to Animal
types using indexed access types. That will clean up the structuring of the types, but it doesn't address the limitation above. A wrapper inside the function, while not very elegant, can take care of that. Here's the whole thing:
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)
Upvotes: 3