在TypeScript中是否存在类似于`keyof`的`valueof`?

sty*_*fle 75 types typescript

我希望能够将对象属性分配给给定键和值作为输入的值,但仍能够确定值的类型.这有点难以解释所以这段代码应该揭示问题:

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
Run Code Online (Sandbox Code Playgroud)

我试过换value: anyvalue: valueof JWT但是没用.

理想情况下,onChange('expire', 1337)会失败,因为1337它不是Date类型.

如何更改value: any为给定键的值?

jca*_*alz 118

更新:看起来问题标题吸引人们寻找所有可能的属性值类型的联合,类似于keyof给出所有可能的属性键类型的联合的方式.让我们先帮助那些人.你可以通过使用带有键的查找类型ValueOf来类似,如下所示:keyofkeyof T

type ValueOf<T> = T[keyof T];
Run Code Online (Sandbox Code Playgroud)

给你的

type Foo = { a: string, b: number };
type ValueOfFoo = ValueOf<Foo>; // string | number
Run Code Online (Sandbox Code Playgroud)

对于上述问题,您可以使用比keyof T其更窄的单个键来仅提取您关注的值类型:

type sameAsString = Foo['a']; // lookup a in Foo
type sameAsNumber = Foo['b']; // lookup b in Foo
Run Code Online (Sandbox Code Playgroud)

为了确保键/值对在函数中正确"匹配",您应该使用泛型和查找类型,如下所示:

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
Run Code Online (Sandbox Code Playgroud)

我们的想法是该key参数允许编译器推断通用K参数.然后它需要value匹配JWT[K],您需要的查找类型.

希望有所帮助; 祝好运!

  • 使用带有函数成员的字符串值枚举时遇到问题。为了很好地处理这个问题,您可以使用“type StringValueOf&lt;T&gt; = T[keyof T] &amp; string;”。我在字符串枚举上找到的最好的文档是 [TypeScript 2.9 发行说明](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html) (3认同)
  • 我发现另一个有用的构造是“Required&lt;T&gt;[keyof T]”,它表示当“t: T”和“k in t”时可以从“t[k]”获取的值。 (2认同)
  • @markokraljevic 答案是完全有效的。您无法将类型分配为对象值,因为运行时不存在 Typescript 类型(非原始类型)。此解决方案用于创建可以是多种类型的类型并将其统一为一种类型,而不是复制/粘贴长类型列表(字符串 | 布尔值 | MyType)。 (2认同)
  • 如果您只是从中获取通用字符串类型,请不要忘记将“as const”添加到相关对象的末尾。 (2认同)

Dim*_*ima 51

还有一种方法可以提取对象的联合类型:

  const myObj = { a: 1, b: 'some_string' } as const;
  type values = typeof myObj[keyof typeof myObj];
Run Code Online (Sandbox Code Playgroud)

结果: 1 | "some_string"

  • 这个 `const` 东西非常有价值,TypeScript 实际上会提供值本身并删除重复项;这对词典非常有用。 (9认同)

Chr*_*ski 32

如果有人仍然valueof为任何目的寻找实现,这是我提出的一个:

type valueof<T> = T[keyof T]
Run Code Online (Sandbox Code Playgroud)

用法:

type actions = {
  a: {
    type: 'Reset'
    data: number
  }
  b: {
    type: 'Apply'
    data: string
  }
}
type actionValues = valueof<actions>
Run Code Online (Sandbox Code Playgroud)

按预期工作:)返回所有可能类型的联合

  • 很好 它几乎解决了[我的其他问题](/sf/ask/3451839261/),但是该开关似乎根本没有缩小类型。 (2认同)

Ame*_*icA 13

您可以为自己创建一个泛型来获取值的类型,但是,请考虑应该声明对象的声明as const,例如:

export const APP_ENTITIES = {
  person: 'PERSON',
  page: 'PAGE',
} as const; <--- this `as const` I meant
Run Code Online (Sandbox Code Playgroud)

然后下面的通用将正常工作:

export type ValueOf<T> = T[keyof T];
Run Code Online (Sandbox Code Playgroud)

现在像下面这样使用它:

const entity: ValueOf<typeof APP_ENTITIES> = 'P...'; // ... means typing

   // it refers 'PAGE' and 'PERSON' to you
Run Code Online (Sandbox Code Playgroud)


Zhe*_*eng 10

尝试这个:

type ValueOf<T> = T extends any[] ? T[number] : T[keyof T]
Run Code Online (Sandbox Code Playgroud)

它适用于数组或普通对象。

// 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...']>
Run Code Online (Sandbox Code Playgroud)

  • 仅适用于 `ReadonlyArray`:`type ValueOf&lt;T&gt; = T extends ReadonlyArray&lt;any&gt; ?T[数字] : T[T 的键];`。请参阅 https://github.com/piotrwitek/utility-types#valuestypet 源 (4认同)

Jos*_*mez 6

使用下面的函数,您可以将值限制为该特定键的值。

function setAttribute<T extends Object, U extends keyof T>(obj: T, key: U, value: T[U]) {
    obj[key] = value;
}
Run Code Online (Sandbox Code Playgroud)

例子

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)
Run Code Online (Sandbox Code Playgroud)


Bil*_*han 5

感谢完美解决问题的现有答案。只是想添加一个包含此实用程序类型的库,如果您更喜欢导入这个常用的。

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>;
Run Code Online (Sandbox Code Playgroud)