Typescript:你能根据参数对象中单个键的值获取函数的返回类型吗?
我有一个 hook,它会进行 API 调用并缓存整个应用中使用的对象。
与其需要有一堆辅助函数来获取这个对象的特定值,不如传入对象的键并返回该键对应的值,该钩子还可以访问这些对象的数组并选择一个特定的对象,或者返回应用的默认值。
钩子函数,我在编写符合 TypeScript 输入和输出的类型定义时没有遇到困难。
这是我目前所做的:
type OddParams = {
thing: number;
thing2: number;
key?: PotatoKey;
}
type Banana = {
id: string;
name: string;
}
type Potato = {
id: string;
name: string;
phone: number;
monkey: Banana;
}
type PotatoKey = keyof Potato;
type Params = OddParams | PotatoKey;
type ObjectType<T> =
T extends unknown ? Potato :
T extends 'id' | 'name' ? string :
T extends 'phone' ? number :
T extends 'monkey' ? Banana :
never;
function getPotato<T extends Params>(desc = (undefined as unknown) as T): ObjectType<T>{
console.log(desc);
return '2' as any
};
getPotato() //returns type Potato (returns the default potato in implemntation )
getPotato('id') //returns type string (would return id of the default potato in implemntation )
getPotato({thing: 2, thing2: 1, key: 'id'}) //returns type string (would return id of potato[2][1] in implemntation )仍然需要将对象部分添加到 ObjectType 变量中。即使只是字符串参数,也在寻找基于键声明的更好方法。上面我对每个键都使用了 T extends 'id' 等写法,但在实际生产问题中有 20 多个键,因此为每个返回类型使用大量三元运算符最终非常难以阅读。
有什么方法可以实现这一点吗?
答案#1
你的getPotato函数基本上需要三种不同的签名,以及三种对应确定匹配返回类型的方法。这可以通过函数重载来实现。
第一和第二个(分别是无参数和字符串参数)相当容易做到:
function getPotato(): Potato;
function getPotato<K extends keyof Potato>(key: K): Potato[K];
对于第三个重载,你需要从你提供的 params 对象中“取出”key 属性的值。在此之前,key 属性必须是 keyof Potato 类型:
type Params = {
thing: number;
thing2: number;
key: keyof Potato;
}
function getPotato<T extends Params>(params: T): Potato[T['key']]
这句话的意思是:params 的类型是 T,它必须是 Params 的特定版本。然后作为返回类型,你可以从 T 中取出键,以找到在 Potato 上对应的类型。
最后,你需要实际的函数实现,这些实现应考虑到这三种情况:
function getPotato(arg?: keyof Potato | Params): Potato | Potato[keyof Potato] {
if (arg === undefined) {
return ... // first overload
}
if (typeof arg === 'string') {
return ... // second overload
}
const { thing, thing2, key } = arg
return ... // third overload
};
把所有内容结合在一起,这给我们以下代码:
type Banana = {
id: string;
name: string;
}
type Potato = {
id: string;
name: string;
phone: number;
monkey: Banana;
}
type Params = {
thing: number;
thing2: number;
key: keyof Potato;
}
function getPotato(): Potato
function getPotato<K extends keyof Potato>(key: K): Potato[K]
function getPotato<T extends Params>(params: T): Potato[T['key']]
function getPotato(arg?: keyof Potato | Params): Potato | Potato[keyof Potato] {
// ...
};
我用以下赋值测试了这些类型:
const a: Potato = getPotato()
const b: string = getPotato('id')
const c: number = getPotato({ thing: 2, thing2: 1, key: 'phone' })
const d: Banana = getPotato('monkey')
