Как я могу определить массив кортежей с различными типами кортежей явно в машинописи? - PullRequest
2 голосов
/ 26 марта 2020

Я пишу конвертер, который преобразует массив кортежей в объект, используя объект поиска, который сообщает функции, какие строки соответствуют каким свойствам. Тем не менее, я не могу найти способ сказать машинописи, что это массив кортежей с указанными c типами, результирующие кортежи всегда являются объединениями. Вот как это выглядит:

interface EndObj {
    a: number; 
    b: string;
    c?: number;
}
interface InitObj {
    d: string;
    e: string;
    f: string;
}

const map = {
    d: 'a',
    e: 'b',
    f: 'c'
} as const;

type MapType = typeof map;
type ResultTuple<T extends keyof InitObj> = [T, EndObj[MapType[T]]];
type ResultTupleArray = ResultTuple<keyof InitObj>[];

const resultObj: ResultTupleArray = [['d', 1], ['e', 3], ['f', 3]]; // invalid! the value of 'e' should only allow strings

Думаю, причина, по которой машинопись позволяет это, заключается в том, что ResultTupleArray определяется с помощью keyof InitObj, поэтому результирующий массив кортежей generi c всегда одинаков, так что T всегда будет одинаковым вместо спецификаций c для каждой записи массива, поэтому его можно описать только с помощью объединения.

Вот как я это выяснил:

const undetected: ResultTuple<keyof InitObj> = ['e', 4]; // should be invalid
const detected: ResultTuple<'e'> = ['e', 4]; // actually shows an error for 4 (Type 'number' is not assignable to type 'string'.)

Для некоторого контекста, вот как выглядит конвертер:

function mapInitToEnd(resultO: ResultTupleArray) {
    const endObj: EndObj = {
        a: -1,
        b: ''
    };

    for (const tuple of resultO) {
        const [key, val] = tuple;
        const mappedKey = map[key];
        endObj[mappedKey] = val;
    }
    return endObj;
}

Есть ли способ сказать машинописи, что generic c действителен только для каждой записи в массиве кортежей, а не для полный массив?

1 Ответ

2 голосов
/ 26 марта 2020

Вы хотите распространить свое определение ResultTuple, чтобы T представляло собой объединение ключей, а результат - объединение кортежей. Самый простой способ выполнить sh, учитывая ваш код, - это сделать определение условно-распределительным условным типом , которое дает вам такое поведение бесплатно:

type ResultTuple<T extends keyof InitObj> = T extends any ? [T, EndObj[MapType[T]]] : never;

Распределительно-условные условные типы запускаются когда у вас есть T extends U ? X : Y, где T - параметр типа. Поэтому, чтобы это произошло выше, мы добавляем бесполезную проверку T extends any ? ... : never. Теперь ваш код выдаст вам ожидаемую ошибку:

const resultObj: ResultTupleArray = [['d', 1], ['e', 3], ['f', 3]];  // error!
// number not assignable to string ----------> ~~~~~~~~

Есть и другие способы получить такое поведение; например, путем построения сопоставленного типа и немедленного поиска его свойств:

type ResultTuple<T extends keyof InitObj> = { [K in T]: [K, EndObj[MapType[K]]] }[T];

Любой способ должен работать.


Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...