Есть ли значение 'valueof', подобное "keyof" в 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

Я попытался изменить value: any value: valueof JWT но это не сработало.

В идеале onChange('expire', 1337) потерпит неудачу, потому что 1337 не является типом даты.

Как изменить value: any значение данного ключа?

Ответ 1

ОБНОВЛЕНИЕ: похоже, заголовок вопроса привлекает людей, ищущих объединение всех возможных типов значений свойств, аналогично тому, как 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']; // lookup a in Foo
type sameAsNumber = Foo['b']; // lookup 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], типом поиска, который вам нужен.

Надеюсь, это поможет; удачи!

Ответ 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>;