TypeScript - 添加动态命名的 属性 到 return 类型
TypeScript - Add dynamically named property to return type
假设我有一个函数接受一个对象、一个键和一个值,然后 returns 一个新对象扩展原始对象添加键和值。
function addKeyValue(obj: Object, key: string, value: any) {
return {
...obj,
[key]: value
}
}
问题:
我怎样才能输入这个函数,这样如果我这样调用它:
const user = addKeyValue({ name: 'Joe' }, 'age', 30);
编译器知道{user}的类型是{ name: string, age: number }
注意:
我知道我可以在没有函数的情况下做到这一点,但是这个问题的目标是用一个函数来完成。 (此示例基于一个更复杂的问题,为简洁起见进行了简化)。
这是我的想法,但它不起作用。 :(
function addKeyValue<TInput>(
obj: TInput,
key: string,
value: any): TInput & { [key]: typeof value } {
return {
...obj,
[key]: value
}
}
从调用者的角度来看,您可能希望签名类似于以下之一:
declare function addKeyValue<T extends {}, K extends keyof any, V>(obj: T, key: K, value: V):
T & Record<K, V>;
const user = addKeyValue({ name: 'Joe' }, 'age', 30); // { name: string; } & Record<"age", number>
user.age // number
user.name // string
这是 TypeScript 2.8 之前的版本,其中交集是表示您正在做的事情的最佳方式。您可能对类型 {name: string} & Record<"age", number>
} 不满意,但它的行为就像您想要的那样。
您也可以使用 conditional types:
declare function addKeyValue2<T extends {}, K extends keyof any, V>(obj: T, key: K, value: V):
{ [P in keyof (T & Record<K, any>)]: P extends K ? V : P extends keyof T ? T[P] : never };
const user2 = addKeyValue2({ name: 'Joe' }, 'age', 30); // { age: number; name: string; }
user2.age // number
user2.name // string
这具有 return 类型的优点,看起来像您期望的那样,但缺点是复杂且可能脆弱(可能并不总是像联合等那样表现)。
这个版本的另一个可能的优点是,如果key
与obj
的键重叠,属性类型将被覆盖:
const other2 = addKeyValue2({ name: 'Joe', age: 30 }, 'name', false);
// { name: boolean; age: number }
而不是相交,这可能不是你想要的:
const whoops = addKeyValue({ name: 'Joe', age: 30 }, 'name', false).name; // never
// boolean & string ==> never
None 使得实现类型检查正确。为此,您应该使用任何 type assertions or overloads 可以完成的工作:
return {
...(obj as any), // simplest way to silence the compiler
[key]: value
}
希望对您有所帮助。祝你好运!
假设我有一个函数接受一个对象、一个键和一个值,然后 returns 一个新对象扩展原始对象添加键和值。
function addKeyValue(obj: Object, key: string, value: any) {
return {
...obj,
[key]: value
}
}
问题: 我怎样才能输入这个函数,这样如果我这样调用它:
const user = addKeyValue({ name: 'Joe' }, 'age', 30);
编译器知道{user}的类型是{ name: string, age: number }
注意: 我知道我可以在没有函数的情况下做到这一点,但是这个问题的目标是用一个函数来完成。 (此示例基于一个更复杂的问题,为简洁起见进行了简化)。
这是我的想法,但它不起作用。 :(
function addKeyValue<TInput>(
obj: TInput,
key: string,
value: any): TInput & { [key]: typeof value } {
return {
...obj,
[key]: value
}
}
从调用者的角度来看,您可能希望签名类似于以下之一:
declare function addKeyValue<T extends {}, K extends keyof any, V>(obj: T, key: K, value: V):
T & Record<K, V>;
const user = addKeyValue({ name: 'Joe' }, 'age', 30); // { name: string; } & Record<"age", number>
user.age // number
user.name // string
这是 TypeScript 2.8 之前的版本,其中交集是表示您正在做的事情的最佳方式。您可能对类型 {name: string} & Record<"age", number>
} 不满意,但它的行为就像您想要的那样。
您也可以使用 conditional types:
declare function addKeyValue2<T extends {}, K extends keyof any, V>(obj: T, key: K, value: V):
{ [P in keyof (T & Record<K, any>)]: P extends K ? V : P extends keyof T ? T[P] : never };
const user2 = addKeyValue2({ name: 'Joe' }, 'age', 30); // { age: number; name: string; }
user2.age // number
user2.name // string
这具有 return 类型的优点,看起来像您期望的那样,但缺点是复杂且可能脆弱(可能并不总是像联合等那样表现)。
这个版本的另一个可能的优点是,如果key
与obj
的键重叠,属性类型将被覆盖:
const other2 = addKeyValue2({ name: 'Joe', age: 30 }, 'name', false);
// { name: boolean; age: number }
而不是相交,这可能不是你想要的:
const whoops = addKeyValue({ name: 'Joe', age: 30 }, 'name', false).name; // never
// boolean & string ==> never
None 使得实现类型检查正确。为此,您应该使用任何 type assertions or overloads 可以完成的工作:
return {
...(obj as any), // simplest way to silence the compiler
[key]: value
}
希望对您有所帮助。祝你好运!