我希望能够将对象属性分配给一个值,给定一个键和值作为输入,但仍然能够确定值的类型。这有点难以解释,所以这段代码应该揭示了问题:
type JWT = { id: string, token: string, expire: Date };
const obj: JWT = { id: 'abc123', token: 'tk01', expire: new Date(2018, 2, 14) };
function print(key: keyof JWT) {
switch (key) {
case 'id':
case 'token':
console.log(obj[key].toUpperCase());
break;
case 'expire':
console.log(obj[key].toISOString());
break;
}
}
function onChange(key: keyof JWT, value: any) {
switch (key) {
case 'id':
case 'token':
obj[key] = value + ' (assigned)';
break;
case 'expire':
obj[key] = value;
break;
}
}
print('id');
print('expire');
onChange('id', 'def456');
onChange('expire', new Date(2018, 3, 14));
print('id');
print('expire');
onChange('expire', 1337); // should fail here at compile time
print('expire'); // actually fails here at run time
我试着将value: any改为value: valueof JWT,但没有成功。
理想情况下,onChange('expire', 1337)会失败,因为1337不是Date类型。
如何将value: any更改为给定键的值?
更新:看起来问题标题吸引人们寻找所有可能的属性值类型的并集,类似于keyof为您提供所有可能的属性键类型的并集。让我们先帮助那些人。你可以让ValueOf类似于keyof,通过使用keyof T作为键的索引访问类型,如下所示:
type ValueOf<T> = T[keyof T];
这让你
type Foo = { a: string, b: number };
type ValueOfFoo = ValueOf<Foo>; // string | number
对于上述问题,您可以使用比keyof T更窄的单个键来提取您所关心的值类型:
type sameAsString = Foo['a']; // look up a in Foo
type sameAsNumber = Foo['b']; // look up b in Foo
为了确保函数中的键/值对正确“匹配”,你应该使用泛型以及索引访问类型,如下所示:
declare function onChange<K extends keyof JWT>(key: K, value: JWT[K]): void;
onChange('id', 'def456'); // okay
onChange('expire', new Date(2018, 3, 14)); // okay
onChange('expire', 1337); // error. 1337 not assignable to Date
其思想是,key形参允许编译器推断泛型K形参。然后,它要求该值与您需要的索引访问类型JWT[K]匹配。
我知道这有点离题了,每次我都在寻找解决方法。我被派到这个岗位。对于那些正在寻找字符串文字类型生成器的人,这里。
这将从对象类型创建一个字符串文字列表。
export type StringLiteralList<T, K extends keyof T> = T[keyof Pick<T, K>];
type DogNameType = { name: "Bob", breed: "Boxer" } | { name: "Pepper", breed: "Spaniel" } | { name: "Polly", breed: "Spaniel" };
export type DogNames = StringLiteralList<DogNameType, "name">;
// type DogNames = "Bob" | "Pepper" | "Polly";
使用下面的函数,可以将值限制为特定键的值。
function setAttribute<T extends Object, U extends keyof T>(obj: T, key: U, value: T[U]) {
obj[key] = value;
}
例子
interface Pet {
name: string;
age: number;
}
const dog: Pet = { name: 'firulais', age: 8 };
setAttribute(dog, 'name', 'peluche') <-- Works
setAttribute(dog, 'name', 100) <-- Error (number is not string)
setAttribute(dog, 'age', 2) <-- Works
setAttribute(dog, 'lastname', '') <-- Error (lastname is not a property)