Недоразумение или ошибка? Проблема с отображенными типами - PullRequest
0 голосов
/ 22 апреля 2020

Может ли кто-нибудь с лучшим пониманием системы типов TypeScript объяснить мне, почему следующее не работает?

export const mySchema = {
  user: {
    email: {type: 'string'},
    name: {type: 'string'},
    age: {type: 'number'},
  },
  message: {
    user_id: {type: 'string'},
    text: {type: 'string'},
  },
} as const;

type TypeMap = {
  string: string;
  number: number;
};

// Causes an error
type Instance<TableName extends keyof typeof mySchema> = {
  [Column in keyof typeof mySchema[TableName]]: TypeMap[typeof mySchema[TableName][Column]['type']]; 
};

// Totally fine
type UserInstance = {
  [Column in keyof typeof mySchema['user']]: TypeMap[typeof mySchema['user'][Column]['type']];
};

Для контекста я пытался создать обобщенные c типы экземпляров для const объект, представляющий схему базы данных.

В TypeScript 3.8.3 я вижу следующую ошибку:

Error:(19, 57) TS2536: Type '"type"' cannot be used to index type '{ readonly user: { readonly email: { readonly type: "string"; }; readonly name: { readonly type: "string"; }; readonly age: { readonly type: "number"; }; }; readonly message: { readonly user_id: { readonly type: "string"; }; readonly text: { ...; }; }; }[TableName][Column]'.

Почему нельзя type использовать для индексации здесь, даже хотя type всегда существует?

Несмотря на это, почему все хорошо, если вместо этого я жестко кодирую TableName?

Заранее спасибо!

1 Ответ

0 голосов
/ 23 апреля 2020

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

Чтобы найти ответ на свой вопрос, я начал с самого общего вида TableName, который можно взять. Это keyof typeof mySchema. Если мы исследуем typeof mySchema[TableName], мы обнаружим, что это недискриминационный союз, поэтому keyof этот тип never.

Отсюда я разветвляюсь на два направления мысли.

1 , Условный тип

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

type InstanceConditionalUnion<TableName extends keyof typeof mySchema> = {
    [Column in keyof typeof mySchema[TableName]]:
        typeof mySchema[TableName][Column] extends {type: any} ?
        TypeMap[typeof mySchema[TableName][Column]['type']] : never; 
};

Тогда InstanceConditionalUnion<'user'> эквивалентно UserInstance, но InstanceConditionalUnion<keyof typeof mySchema> равно {}.

2. Тип пересечения

Чтобы использовать что-то вроде Instance<keyof typeof mySchema>, мы можем преобразовать объединение в пересечение следующим образом:

type InstanceConditionalIntersection<TableName extends keyof typeof mySchema> = {
    [Column in keyof UnionToIntersection<typeof mySchema[TableName]>]:
        UnionToIntersection<typeof mySchema[TableName]>[Column] extends {type: any} ?
        TypeMap[UnionToIntersection<typeof mySchema[TableName]>[Column]['type']] : never;
};

type UnionToIntersection<U> = 
    (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

С UnionToIntersection, взятым из этот ответ .

В этом случае InstanceConditionalIntersection<keyof typeof mySchema> содержит все ключи как user, так и message

Увы, этот вид подводит меня к точке, где мой ответ не работает, потому что InstanceIntersection сталкивается с та же проблема, что и ваш оригинальный тип.

type InstanceIntersection<TableName extends keyof typeof mySchema> = {
    [Column in keyof UnionToIntersection<typeof mySchema[TableName]>]:
        TypeMap[UnionToIntersection<typeof mySchema[TableName]>[Column]['type']];
}; 
// this is an error. UnionToIntersection<typeof mySchema[TableName]>[Column]
// doesn't have a 'type' property.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...