Сопоставление типов кортежей внутри типов кортежей

Документы для отображаемые типы кортежей предлагают следующий пример:

type MapToPromise<T> = { [K in keyof T]: Promise<T[K]> };

type Coordinate = [number, number]

type PromiseCoordinate = MapToPromise<Coordinate>; // [Promise<number>, Promise<number>]

Но что, если исходный кортеж содержит другие кортежи, и в рамках отображения я хочу извлечь интересные значения из этих внутренних кортежей?

e.g.

// I just want the numbers, but there is other stuff in the type
type MessyCoordinate = [
    [string, number],
    [string, number]
]

Я ожидал, что смогу сделать это:

type Clean<T extends [string, number]> = T[1]

// Why doesn't this work?
type MapToClean<T> = { [K in keyof T]: Clean<T[K]> }

type CleanCoordinate = MapToClean<MessyCoordinate>; // [number, number]

Компилятор выдает мне эту ошибку:

Type 'T[K]' does not satisfy the constraint '[string, number]'.
  Type 'T[keyof T]' is not assignable to type '[string, number]'.
    Type 'T[string] | T[number] | T[symbol]' is not assignable to type '[string, number]'.
      Type 'T[string]' is not assignable to type '[string, number]'.(2344)

Я могу попробовать добавить больше ограничений на MapToClean, но это не помогает:

type MapToClean2<T extends [string, number][]> = { [K in keyof T]: Clean<T[K]> }

Я также могу избавиться от типа утилиты Clean и просто сделать это, но это тоже не работает:

// Why doesn't this work?
type MapToClean<T> = { [K in keyof T]: T[K][1] }
// Type '1' cannot be used to index type 'T[K]'.

type CleanCoordinate = MapToClean<MessyCoordinate>; // [number, number]

Является ли мое фундаментальное ожидание, что я смогу взломать кортеж внутри сопоставления, ошибочно? Или мне просто не хватает какого-то волшебного синтаксиса, чтобы T[K] выглядело как [string, number]?

Playground link


person Matt Wynne    schedule 05.06.2020    source источник


Ответы (2)


Очевидная проблема заключается в том, что T в MapToClean<T> не является ограниченным к типу, свойства которого равны [string, number]. Вы можете добавить соответствующее ограничение, и тогда оно будет работать:

type MapToClean<T extends { [K in keyof T]: [string, number] }> =
    { [K in keyof T]: Clean<T[K]> }

Менее очевидная проблема заключается в том, почему ограничение, которое специально требует, чтобы T было типом массива, по-прежнему не работает:

type MapToCleanOops<T extends [string, number][]> =
    { [K in keyof T]: Clean<T[K]> } // same error

Эта проблема является нерешенной ошибкой в ​​TypeScript, microsoft/TypeScript#27995< /а>. Когда вы сопоставляете кортеж с сопоставленным типом, вы получаете кортеж. Но компилятор не понимает этого внутри определения сопоставленного типа: см. это комментарий. На данный момент обходным путем было бы сделать что-то, чтобы сообщить компилятору, что свойство будет иметь ожидаемый тип; обычно с типом утилиты Extract:

type MapToCleanOkayAgain<T extends [string, number][]> =
    { [K in keyof T]: Clean<Extract<T[K], [string, number]>> } // okay again

type CleanCoordinate2 = MapToCleanOkayAgain<MessyCoordinate>; // [number, number]

Вы могли бы использовать Extract, чтобы исправить исходный MapToClean, не ограничивая T:

type MapToCleanOkayUnconstrained<T> =
    { [K in keyof T]: Clean<Extract<T[K], [string, number]>> } // still okay

type CleanCoordinate3 = MapToCleanOkayUnconstrained<MessyCoordinate>; // [number, number]

Но вы, вероятно, хотите это ограничение, иначе оно позволит вам передавать странные вещи:

type BeCareful = MapToCleanOkayUnconstrained<{ a: 1, b: ["", 4], c: false }>;
/* type BeCareful = { a: never; b: 4; c: never; } */

Тебе решать.


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

Ссылка на код Playground

person jcalz    schedule 06.06.2020
comment
Спасибо, это было именно то, что мне было нужно. - person Matt Wynne; 07.06.2020
comment
У меня есть дополнительный вопрос, но, возможно, мне следует задать его отдельно? Я был удивлен, обнаружив, что значение этого отображаемого типа не является итерируемым (должен иметь метод '[Symbol.iterator]()', который возвращает итератор.). Он работает на игровой площадке, но не на моей локальной машине (работает tsc 3.9.3). - person Matt Wynne; 07.06.2020

Если вы заранее знаете, что будете отображать MessyCoordinate, чтобы получить number, то зачем усложнять себе задачу?

type MessyCoordinate = [
    [string, number],
    [string, number]
]
type MapToClean<T> = { [K in keyof T]: number }
type CleanCoordinate = MapToClean<MessyCoordinate>; // [number, number]
person Rachid Oussanaa    schedule 06.06.2020
comment
Может быть, MapToClean<[["",1],["",2],["",3]]> должно давать [1,2,3], а не [number, number, number]? - person jcalz; 06.06.2020
comment
@jcalz зависит от того, чего хочет ОП, это не то, что я понял из его вопроса. - person Rachid Oussanaa; 06.06.2020
comment
мой ответ отвечает его конкретной потребности, это не общий ответ на проблему, которую он поднял - person Rachid Oussanaa; 06.06.2020
comment
Я упростил задачу, чтобы упростить понимание, извините. У меня есть общий вопрос о возможности извлечения элементов из внутреннего кортежа. В моем случае использования я не буду знать заранее, что это за типы, и они будут отличаться друг от друга. - person Matt Wynne; 06.06.2020