如何使用联合类型作为函数参数
How to use union type as function parameter
在下面的示例中,我创建了一个联合类型(字符串或数字)。然后我创建了一个函数签名,它应该接受一个字符串或数字作为输入。但是现在我不能使用这个函数定义
type AB = string | number
type X = (inp: AB) => void;
const x: X = (inp: number) => { console.log('ok')}; // <-- ERROR
const y: X = (inp: string) => { console.log('ok')}; // <- ERROR
因为常量x和y有错误
类型 AB
可以更改,可以添加更多类型,所以如果我可以在定义类型 X
中使用它,然后为 [=] 中定义的每个类型创建实现,我会很好11=]。但是我该怎么做呢?
通过将 generic type parameter 与 X
结合使用,您甚至不需要在实现中键入参数:
type AB = string | number;
type X<T extends AB> = (inp: T) => void;
const x: X<number> = (inp) => {
inp // number
};
const y: X<string> = (inp) => {
inp // string
};
编辑:对新问题的回应:
Note that idiomatically-named string enums use PascalCase.
查看代码中的注释:
// Base types:
/**
* From [Distributive conditional types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types):
*
* > ...an instantiation of `T extends U ? X : Y` with
* > the type argument `A | B | C` for `T` is resolved as
* > `(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)`.
*
* For `T extends unknown ? ...`, see also:
* https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#example-3
*/
type DistributeUnionToFirstParamInFn<
U,
FnReturnType = void,
> = U extends unknown ? ((_: U) => FnReturnType) : never;
type AB = string | number;
const enum Z { X = 'x', Y = 'y'};
// First example:
type MyObj = Record<Z, DistributeUnionToFirstParamInFn<AB>>;
const obj1: MyObj = {
x: (inp: string) => { console.log('this is a string ', inp) },
y: (inp: number) => { console.log('this is a number ', inp) },
};
type ParamInObj1X = Parameters<typeof obj1['x']>[0]; // string | number
type ParamInObj1Y = Parameters<typeof obj1['y']>[0]; // string | number
// So, not quite what you want. Compare with...
//Second example:
function validateObject <T extends Record<Z, DistributeUnionToFirstParamInFn<AB>>>(value: T): T {
return value;
}
validateObject({x: (inp: string) => { console.log('this is a string ', inp) }}); /*
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Property 'y' is missing in type... Error (2345) */
validateObject({
x: (inp: string) => { console.log('this is a string ', inp) },
y: (inp: boolean) => { console.log('this is a number ', inp) }, /*
^
Type '(inp: boolean) => void' is not assignable to type '((_: string) => void) | ((_: number) => void)'... Error (2322) */
});
const obj2 = validateObject({
x: (inp: string) => { console.log('this is a string ', inp) },
y: (inp: number) => { console.log('this is a number ', inp) },
});
type ParamInObj2X = Parameters<typeof obj2['x']>[0]; // string
type ParamInObj2Y = Parameters<typeof obj2['y']>[0]; // number
//
在下面的示例中,我创建了一个联合类型(字符串或数字)。然后我创建了一个函数签名,它应该接受一个字符串或数字作为输入。但是现在我不能使用这个函数定义
type AB = string | number
type X = (inp: AB) => void;
const x: X = (inp: number) => { console.log('ok')}; // <-- ERROR
const y: X = (inp: string) => { console.log('ok')}; // <- ERROR
因为常量x和y有错误
类型 AB
可以更改,可以添加更多类型,所以如果我可以在定义类型 X
中使用它,然后为 [=] 中定义的每个类型创建实现,我会很好11=]。但是我该怎么做呢?
通过将 generic type parameter 与 X
结合使用,您甚至不需要在实现中键入参数:
type AB = string | number;
type X<T extends AB> = (inp: T) => void;
const x: X<number> = (inp) => {
inp // number
};
const y: X<string> = (inp) => {
inp // string
};
编辑:对新问题的回应:
Note that idiomatically-named string enums use PascalCase.
查看代码中的注释:
// Base types:
/**
* From [Distributive conditional types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types):
*
* > ...an instantiation of `T extends U ? X : Y` with
* > the type argument `A | B | C` for `T` is resolved as
* > `(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)`.
*
* For `T extends unknown ? ...`, see also:
* https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#example-3
*/
type DistributeUnionToFirstParamInFn<
U,
FnReturnType = void,
> = U extends unknown ? ((_: U) => FnReturnType) : never;
type AB = string | number;
const enum Z { X = 'x', Y = 'y'};
// First example:
type MyObj = Record<Z, DistributeUnionToFirstParamInFn<AB>>;
const obj1: MyObj = {
x: (inp: string) => { console.log('this is a string ', inp) },
y: (inp: number) => { console.log('this is a number ', inp) },
};
type ParamInObj1X = Parameters<typeof obj1['x']>[0]; // string | number
type ParamInObj1Y = Parameters<typeof obj1['y']>[0]; // string | number
// So, not quite what you want. Compare with...
//Second example:
function validateObject <T extends Record<Z, DistributeUnionToFirstParamInFn<AB>>>(value: T): T {
return value;
}
validateObject({x: (inp: string) => { console.log('this is a string ', inp) }}); /*
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Property 'y' is missing in type... Error (2345) */
validateObject({
x: (inp: string) => { console.log('this is a string ', inp) },
y: (inp: boolean) => { console.log('this is a number ', inp) }, /*
^
Type '(inp: boolean) => void' is not assignable to type '((_: string) => void) | ((_: number) => void)'... Error (2322) */
});
const obj2 = validateObject({
x: (inp: string) => { console.log('this is a string ', inp) },
y: (inp: number) => { console.log('this is a number ', inp) },
});
type ParamInObj2X = Parameters<typeof obj2['x']>[0]; // string
type ParamInObj2Y = Parameters<typeof obj2['y']>[0]; // number
//