我希望能够将对象属性分配给给定键和值作为输入的值,但仍然能够确定值的类型。这有点难以解释,所以这段代码应该可以揭示问题:
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
为您提供所有可能的属性键类型的联合的方式。让我们先帮助那些人。您可以使用 indexed access types 和 keyof T
作为键来制作类似于 keyof
的 ValueOf
,如下所示:
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
为了确保键/值对在函数中正确“匹配”,您应该使用 generics 以及索引访问类型,如下所示:
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
参数。然后它要求 value
匹配 JWT[K]
,即您需要的索引访问类型。
还有另一种提取对象联合类型的方法:
const myObj = { a: 1, b: 'some_string' } as const;
type values = typeof myObj[keyof typeof myObj];
结果:1 | "some_string"
const
这个东西很有价值,TypeScript 实际上会自己提供值并删除重复项;这对字典非常有用。
type
处理,则可以写成 type MyEnum = { A: 1, B: 'some_string' }; type values = MyEnum[keyof MyEnum];
如果有人出于任何目的仍在寻找 valueof
的实现,这是我想出的一个:
type valueof<T> = T[keyof T]
用法:
type actions = {
a: {
type: 'Reset'
data: number
}
b: {
type: 'Apply'
data: string
}
}
type actionValues = valueof<actions>
按预期工作:) 返回所有可能类型的联合
使用下面的函数,您可以将值限制为该特定键的值。
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)
尝试这个:
type ValueOf<T> = T extends any[] ? T[number] : T[keyof T]
它适用于数组或普通对象。
// type TEST1 = boolean | 42 | "heyhey"
type TEST1 = ValueOf<{ foo: 42, sort: 'heyhey', bool: boolean }>
// type TEST2 = 1 | 4 | 9 | "zzz..."
type TEST2 = ValueOf<[1, 4, 9, 'zzz...']>
ReadonlyArray
:type ValueOf<T> = T extends ReadonlyArray<any> ? T[number] : T[keyof T];
。请参阅 github.com/piotrwitek/utility-types#valuestypet 来源
感谢完美解决问题的现有答案。只是想添加一个包含此实用程序类型的库,如果您更喜欢导入这个通用的。
https://github.com/piotrwitek/utility-types#valuestypet
import { ValuesType } from 'utility-types';
type Props = { name: string; age: number; visible: boolean };
// Expect: string | number | boolean
type PropsValues = ValuesType<Props>;
您可以为自己创建一个 Generic 来获取值的类型,但是,请考虑对象的声明应该声明为 as const
,例如:
export const APP_ENTITIES = {
person: 'PERSON',
page: 'PAGE',
} as const; <--- this `as const` I meant
然后下面的泛型将正常工作:
export type ValueOf<T> = T[keyof T];
现在像下面这样使用它:
const entity: ValueOf<typeof APP_ENTITIES> = 'P...'; // ... means typing
// it refers 'PAGE' and 'PERSON' to you
您可以使用泛型的帮助来定义作为 JWT 的键的 T
和类型为 JWT[T]
的值
function onChange<T extends keyof JWT>(key: T, value: JWT[T]);
这里唯一的问题是在实现中,后面的 obj[key] = value + ' (assigned)';
将不起作用,因为它会尝试将 string
分配给 string & Date
。此处的解决方法是将索引从 key
更改为 token
,以便编译器知道目标变量类型是 string
。
解决此问题的另一种方法是使用 Type Guard
// IF we have such a guard defined
function isId(input: string): input is 'id' {
if(input === 'id') {
return true;
}
return false;
}
// THEN we could do an assignment in "if" block
// instead of switch and compiler knows obj[key]
// expects string value
if(isId(key)) {
obj[key] = value + ' (assigned)';
}
单线:
type ValueTypesOfPropFromMyCoolType = MyCoolType[keyof MyCoolType];
通用方法的示例:
declare function doStuff<V extends MyCoolType[keyof MyCoolType]>(propertyName: keyof MyCoolType, value: V) => void;
type StringValueOf<T> = T[keyof T] & string;
。我在字符串枚举中找到的最好的文档是 TypeScript 2.9 release notesRequired<T>[keyof T]
,它表示在t: T
和k in t
时可以从t[k]
获得的值。