【问题标题】:Is there a `valueof` similar to `keyof` in TypeScript?TypeScript 中是否有类似于 `keyof` 的`valueof`?
【发布时间】:2018-08-23 11:05:14
【问题描述】:

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

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 更改为给定键的值?

【问题讨论】:

  • 包 type-fest (github.com/sindresorhus/type-fest) 具有 ValueOf 类型,以及许多其他非常有用的实用程序类型 - 我一直使用它,并且强烈推荐它。

标签: typescript types


【解决方案1】:

更新:看起来问题标题吸引了人们寻找所有可能的属性值类型的联合,类似于keyof 为您提供所有可能的属性键类型的联合。让我们先帮助那些人。您可以使用indexed access typeskeyof T 作为键来制作类似于keyofValueOf,如下所示:

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]

【讨论】:

  • 在使用带有函数成员的字符串值枚举时遇到问题。为了处理好这个,你可以使用type StringValueOf&lt;T&gt; = T[keyof T] &amp; string;。我在字符串枚举中找到的最好的文档是 TypeScript 2.9 release notes
  • 我发现另一个有用的构造是Required&lt;T&gt;[keyof T],它表示当t: Tk in t 时可以从t[k] 获得的值。
  • 这个答案没用,我也想用{ a: string, b: number };作为对象,就像类型一样
  • @markokraljevic 答案是完全有效的。您不能将类型分配为对象值,因为 Typescript 类型(非原始类型)在运行时不存在。此解决方案用于创建可以是多种类型的类型并将其统一为一个类型,而不是复制/粘贴长类型列表(字符串 | 布尔值 | MyType)。
【解决方案2】:

如果有人出于任何目的仍在寻找valueof 的实现,这是我想出的一个:

type valueof<T> = T[keyof T]

用法:

type actions = {
  a: {
    type: 'Reset'
    data: number
  }
  b: {
    type: 'Apply'
    data: string
  }
}
type actionValues = valueof<actions>

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

【讨论】:

    【解决方案3】:

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

    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>;
    

    【讨论】:

      【解决方案4】:

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

        const myObj = { a: 1, b: 'some_string' } as const;
        type values = typeof myObj[keyof typeof myObj];
      

      结果:1 | "some_string"

      【讨论】:

      • const 这个东西很有价值,TypeScript 实际上会自己提供值并删除重复项;这对字典非常有用。
      【解决方案5】:

      试试这个:

      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...']>
      

      【讨论】:

      【解决方案6】:

      单线:

      type ValueTypesOfPropFromMyCoolType = MyCoolType[keyof MyCoolType];
      

      泛型方法示例:

      declare function doStuff<V extends MyCoolType[keyof MyCoolType]>(propertyName: keyof MyCoolType, value: V) => void;
      

      【讨论】:

        【解决方案7】:

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

        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)
        

        【讨论】:

        • 为拉丁美洲的名字点赞:)
        【解决方案8】:

        您可以使用泛型的帮助来定义 T,它是 JWT 的键,值是 JWT[T] 类型

        function onChange<T extends keyof JWT>(key: T, value: JWT[T]);
        

        这里唯一的问题是在实现中跟随obj[key] = value + ' (assigned)'; 将不起作用,因为它会尝试将string 分配给string &amp; 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)';
        }
        

        【讨论】:

          【解决方案9】:

          您可以为自己创建一个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
          

          【讨论】:

            猜你喜欢
            • 2016-01-13
            • 2014-10-06
            • 2018-12-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-12
            • 2011-11-29
            相关资源
            最近更新 更多