您总是可以通过狡猾(例如,索引访问和假设R[key]
是读写的编译器)来根据您的意愿弯曲类型系统
function getUpdateData<R extends BaseRecord>(record: R, key: keyof R, newItem: string) {
var updateData: Partial<R> = {};
updateData[key] = [...record[key], newItem];
return updateData
}
或蛮力(通过any
类型):
function getUpdateData<R extends BaseRecord>(record: R, key: keyof R, newItem: string) {
const updateData: Partial<R> = <any> { [key]: [...record[key], newItem] }
return updateData
}
以上回答了您的问题,但请注意:此功能不安全。它假定任何record
传入的属性都有一个string[]
值key
,但类型R
可能没有。例如:
interface EvilRecord extends BaseRecord {
e: number;
}
var evil: EvilRecord = { a: ['hey', 'you'], e: 42 };
getUpdateData(evil, 'e', 'kaboom'); // compiles okay but runtime error
此外,返回值类型Partial<R>
有点太宽了:你知道它会有key
键,但你需要检查它以使类型系统满意:
var updatedData = getUpdateData<DerivedRecord>(record, "c", "first item in c") // Partial<DerivedRecord>
updatedData.c[0] // warning, object is possibly undefined
我建议getUpdateData()
这样输入:
type KeyedRecord<K extends string> = {
readonly [P in K]: ReadonlyArray<string>
};
function getUpdateData<K extends string, R extends KeyedRecord<K>=KeyedRecord<K>>(record: R, key: K, newItem: string) {
return <KeyedRecord<K>> <any> {[key as string]: [...record[key], newItem]};
}
(请注意,由于TypeScript中的错误,这仍然很难正确输入)现在该函数将只接受key
属性为 type 的内容ReadonlyArray<string>
,并保证该key
属性存在于返回值中:
var evil: EvilRecord = { a: ['hey', 'you'], e: 42 };
getUpdateData(evil, 'e', 'kaboom'); // error, number is not a string array
var updatedData = getUpdateData(record, "c", "first item in c") // KeyedRecord<"c">
updatedData.c[0] // no error
希望有帮助。
技术更新
我将getUpdateData()
上面建议的声明更改为具有两个泛型参数,因为出于某种原因,TypeScript 之前为参数推断出过宽的类型key
,从而迫使您在调用站点指定键类型:
declare function oldGetUpdateData<K extends string>(record: KeyedRecord<K>, key: K, newItem: string): KeyedRecord<K>;
oldGetUpdateData(record, "c", "first item in c"); // K inferred as 'a'|'b'|'c', despite the value of 'c'
oldGetUpdateData<'c'>(record, "c", "first item in c"); // okay now
通过添加第二个泛型,我显然延迟了 TypeScript 在正确推断出键类型之后对记录类型的推断:
getUpdateData(record, "c", "hello"); // K inferred as 'c' now
随意忽略这一点,但这就是使用启发式类型推断制作香肠的方式。