TypeScript 中是否有类似于 \`keyof\` 的\`valueof\`?

新手上路,请多包涵

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

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

原文由 styfle 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 730
2 个回答

更新:看起来问题标题吸引了人们寻找所有可能的属性值类型的联合,类似于 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 参数。然后它要求 value 匹配 JWT[K] ,您需要的索引访问类型。

原文由 jcalz 发布,翻译遵循 CC BY-SA 4.0 许可协议

使用 type-fest lib,您可以使用 ValueOf 来做到这一点:

 import type { ValueOf } from 'type-fest';

export const PATH_NAMES = {
  home: '/',
  users: '/users',
  login: '/login',
  signup: '/signup',
};

interface IMenu {
  id: ValueOf<typeof PATH_NAMES>;
  label: string;
  onClick: () => void;
  icon: ReactNode;
}

  const menus: IMenu[] = [
    {
      id: PATH_NAMES.home,
      label: t('common:home'),
      onClick: () => dispatch(showHome()),
      icon: <GroupIcon />,
    },
    {
      id: PATH_NAMES.users,
      label: t('user:users'),
      onClick: () => dispatch(showUsers()),
      icon: <GroupIcon />,
    },
  ];

原文由 Tiavina MIchael 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进