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

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 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 />,
    },
  ];

其他回答

感谢现有的答案,完美地解决了这个问题。只是想添加一个库已经包括这个实用程序类型,如果你更喜欢导入这个常见的。

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

一行程序:

type ValueTypesOfPropFromMyCoolType = MyCoolType[keyof MyCoolType];

泛型方法示例:

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

使用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 />,
    },
  ];

如果有人还在为任何目的寻找valueof的实现,这是我想到的:

type valueof<T> = T[keyof T]

用法:

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

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

更新:看起来问题标题吸引人们寻找所有可能的属性值类型的并集,类似于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]匹配。