好的,所以我有这个功能,应该从一个对象接收一个键和一个值。不同的键具有与之关联的不同的值类型。
我不想有一个通用的功能,例如:
function updateField(key: string, value: any) {
}
例如,我在这里有这个对象:
interface ISessionSpecific {
students?: number[];
subject?: number;
address?: string;
duration?: string[];
date?: Date;
}
我想创建一个更新此对象中字段的函数,但我希望它正确键入...
所以我打电话给
const value = something;
updateField('address', value);
如果值不是 string 类型,则会抛出错误;
我尝试过的事情:
// First Approach:
type ForField<T extends keyof ISessionSpecific> = {
field: T;
value: Required<ISessionSpecific>[T];
};
type Approach1 =
| ForField<'address'>
| ForField<'students'>
| ForField<'date'>
| ForField<'duration'>
| ForField<'subject'>;
// Second Approach
type Approach2<T extends ISessionSpecific = ISessionSpecific> = {
field: keyof T;
value: T[keyof T];
};
// Testing
const x: [Approach1, Approach2] = [
{ field: 'address', value: 0 }, // Error
{ field: 'address', value: 0 }, // No Error
];
我的第一个方法解决了我的问题,但我认为这太冗长了。因为我创建的这个接口只是一个示例,所以实际的接口可能更大。 所以我想知道是否有任何更优雅的方法可以做到这一点
答案 0 :(得分:2)
您可以利用distributive conditional types来获得与Approach1
类似的自动生成的类型:
type MapToFieldValue<T, K = keyof T> = K extends keyof T ? { field: K, value: T[K] } : never;
const foo: MapToFieldValue<ISessionSpecific> = { field: 'address', value: 0 } // Expect error;
MapToFieldValue<ISessionSpecific>
的结果将等于:
type ManualMap = {
field: "students";
value: number[] | undefined;
} | {
field: "subject";
value: number | undefined;
} | {
field: "address";
value: string | undefined;
} | {
field: "duration";
value: string[] | undefined;
} | {
field: 'date';
value: Date | undefined;
}
使用映射类型的另一种方法会产生相同的结果(感谢@Titian):
type MapToFieldValue<T> = { [K in keyof T]: { field: K, value: T[K] } }[keyof T]
答案 1 :(得分:1)
interface ISessionSpecific {
students?: number[];
subject?: number;
address?: string;
duration?: string[];
date?: Date;
}
function updateField<T, P extends keyof T>(obj: T, prop: P, value: T[P]) {
obj[prop] = value;
}
var myObj: ISessionSpecific = {};
updateField(myObj, "date", new Date()); // OK!
updateField(myObj, "nonExistingProp", "some Value"); // Error "nonExistingProp" is not a valid prop.
let subject1 = 10; // inferred type : number
let subject2 = "10"; // inferred type : string
updateField(myObj, "subject", subject1); // OK!
updateField(myObj, "subject", subject2); // Error Argument of type 'string' is not assignable to parameter of type 'number | undefined'.
updateField(myObj, "subject", undefined); // OK because ISessionSpecific has subject as optional
// if you want the 3rd paramater to be not null or undefined you need to do this:
function updateFieldNotUndefined<T, P extends keyof T>(obj: T, prop: P, value: Exclude<T[P], null | undefined>) {
obj[prop] = value;
}
updateFieldNotUndefined(myObj, "subject", undefined); // ERROR!
// If you want to pass an object of Key-Value pairs:
function updateFieldKeyValuePair<T, P extends keyof T>(
obj: T,
kvp: { prop: P, value: Exclude<T[P], null | undefined> }
) {
obj[kvp.prop] = kvp.value;
}
// if you want to put it in a class:
class SessionSpecific implements ISessionSpecific {
students?: number[];
subject?: number;
address?: string;
duration?: string[];
date?: Date;
public updateField<P extends keyof this>(prop: P, value: this[P]) {
this[prop] = value;
}
}