Reputation: 19839
I have an api with a typed map from api name to api input object type:
type ApiInputTypes = {
doThis: { x: number }
doThat: { y: string }
}
type ApiNames = keyof ApiInputTypes
My goal is to write a generic function that can handle any of these api requests, but I'm getting a type error where I would not expect it.
function handleApi<Name extends ApiNames>(name: Name, args: ApiInputTypes[Name]) {
if (name === "doThis") {
args.x // Error
} else {
args.y // Error
}
}
Ironically, this still works though...
// But this works...
handleApi("doThis", { x: 190 })
Any ideas? Here's a playground link.
Upvotes: 1
Views: 60
Reputation: 328132
Control flow analysis currently does not narrow generic type parameters (see microsoft/TypeScript#24085). It looks like this issue has been reported multiple times with multiple suggestions for dealing with it. Nothing's been implemented yet. For now there are just workarounds.
One possible workaround is to package name
and args
into a single value nameAndArgs
, whose type you specify as a union of the two possible cases.
type ValueOf<T> = T[keyof T];
type NameAndArgs = ValueOf<{ [N in ApiNames]: { name: N, args: ApiInputTypes[N] } }>;
function handleApi<Name extends ApiNames>(name: Name, args: ApiInputTypes[Name]) {
const nameAndArgs = { name, args } as NameAndArgs;
if (nameAndArgs.name === "doThis") {
nameAndArgs.args.x // okay
} else {
nameAndArgs.args.y // okay
}
}
Note that the NameAndArgs
property is equivalent to
type NameAndArgs = {
name: "doThis";
args: {
x: number;
};
} | {
name: "doThat";
args: {
y: string;
};
}
The function works because the single nameAndArgs
variable can be narrowed, and the name
and args
properties stay narrowed in the relevant clauses.
Upvotes: 5
Reputation: 189
Try storing the types in an enum
export enum ApiInputTypes {
doThis = 'doThis',
doThat = 'doThat'
}
function handleApi(name: ApiInputTypes) {
if(name === ApiInputTypes.doThis){
//doThis
}
}
Upvotes: 0