Как сопоставленные типы работают с базовыми типами? - PullRequest
0 голосов
/ 13 июля 2020

Я не могу понять, почему в приведенной ниже ситуации type T0 - это строка, а не объект с ключами "toString", "slice", "split" ... и почему type T1 - это объект с типом ключа string в то время как мы теперь, что keyof any string | symbol | number?

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

type T0 = MappedType<string>;
type T1 = MappedType<any>;

1 Ответ

2 голосов
/ 13 июля 2020

Авторитетный ответ для T0 можно найти в запросе на вытягивание , в котором реализованы «homomorphi c сопоставленные типы » или сопоставленные типы формы {[K in keyof T]: ...}, где T является параметром общего типа c. (Обратите внимание, что изначально он назывался «isomorphi c» вместо «homomorphi c», но это то же самое.)

Обычный вариант использования такого сопоставленного типа - это когда T - некоторый тип объекта; в этом случае отображение сохраняет имена ключей из T, а также их модификаторы «только для чтения» и «необязательности». (Можно переопределить эти модификаторы, явно установив их в сопоставленном типе.)

Итак, что происходит, когда T является примитивным типом, например string? В запросе на вытягивание говорится: «Мы просто создаем этот примитивный тип». Это оказывается очень полезным, особенно в случаях, когда вы рекурсивно используете сопоставленные типы. Тип, такой как

type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> }

, будет проходить через тип вложенного объекта и делать каждое подсвойство необязательным ... и оставлять примитивы в покое :

declare const ab: DeepPartial<{ a: { b: string } }>;
const str = ab.a!.b!; // string

Это почти всегда то, чего хотят люди. Если бы такой тип продолжал возвращаться вниз по всем свойствам обернутых примитивов, таких как length и toUpperCase, вы бы получили в основном бесполезный тип. И тогда единственный способ получить "нормальную" версию - это использовать условные типы для завершения спуска вместо примитивов отображения:

type DeepPartial2<T> = 
  { [K in keyof T]?: T[K] extends object ? DeepPartial2<T[K]> : T[K] }

, что менее удобно (и не так ли? Это возможно до тех пор, пока TS2.8 не представит условные типы).

В некотором смысле люди обычно хотят сохранить «ту же общую форму» для объектов, отображаемых гомоморфно, но то, что означает «та же общая форма», не всегда очевидно. Типы объектов остаются типами объектов. Примитивы остаются примитивами. Первоначально массивы рассматривались как обычные типы объектов, создавая почти бесполезный тип, который преобразовывал методы массива во что-то неузнаваемое. Это никому не понравилось, поэтому TypeScript 3.1 изменил его , так что сопоставленные типы массивов по-прежнему являются типами массивов, а сопоставленные типы кортежей по-прежнему являются типами кортежей.

Остается T1, где отображение homomorphi c по свойствам any дает индексируемый тип.

Ранее связанный запрос на вытягивание не go в него, но разработчик объяснил свои рассуждения в комментарии к проблеме GitHub , сообщив об этом как об ошибке:

keyof any равно string | number | symbol, поэтому сопоставленный тип, примененный к any, дает эквивалент { [x: string]: XXX }.

Обратите внимание, что он говорит «эквивалент». symbol автоматически удаляется из сопоставленных типов, поэтому {[K in "a" | symbol]: string} дает {a: string}. Вам разрешено иметь индексы string и number по отдельности, поэтому {[K in string | number]: string} равно {[k: string]: string; [k: number]: string}. Но поскольку ключи numeri c считаются строковыми в TypeScript и JavaScript, это то же самое, что {[k: string]: string}. (Если вы хотите узнать больше о том, что происходит с ключами string, number и symbol при сопоставлении, вы можете увидеть эту документацию .)

Лично я Я не на 100% на борту с keyof any, сопоставленным только с string индексом вместо string и number, поскольку {[K in keyof T]: K} покажет разницу ... но это второстепенный момент.

Важный момент взят из оставшейся части комментария:

Вы можете возразить, что мы должны просто дать any, когда сопоставленный тип homomorphi c применяется к any, но это было бы менее точно. Например, { [K in keyof T]: number }, созданный с помощью T, поскольку any действительно должен давать { [x: string]: number }, а не только any.

Итак, поехали; any рассматривается как тип объекта с ключами, индексируемыми строкой, так что сопоставление с ним дает что-то разумное. И этот комментарий является наиболее близким к каноническому ответу на этот вопрос, который я видел.

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

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

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