Как мне указать объект типа, который должен быть другого типа?

#typescript

#typescript

Вопрос:

Я хочу заставить объект типа удовлетворять шаблону типа следующим образом:

 type Color = string
type Intent = 'primary' | 'secondary' | 'error'

// This doesn't work
type IntentColors: Record<Intent, Color> = {
  primary: '#fff' | 'white';
  secondary: '#000' | 'black';
  error: '#ff0000' | 'pink';
}
  

Ответ №1:

Поскольку Record<Intent, Color> это именованный тип объекта, ключи которого статически известны во время компиляции, вы можете interface расширить его:

 interface IntentColors extends Record<Intent, Color> {
  primary: '#fff' | 'white';
  secondary: '#000' | 'black';
  error: '#ff0000' | 'pink';
}
  

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

 interface BadIntentColors extends Record<Intent, Color> { // error!
  primary: '#fff' | 'white';
  secondary: number;  // oops
  error: '#ff0000' | 'pink';
}
  

Если вы не укажете primary , secondary или error в объявлении, они останутся string , потому что именно так работает расширение интерфейса ( interface Bar extends Foo {} означает, что Bar имеет те же свойства, что и Foo ). Это может быть проблемой, если вы неправильно напишете свойство, поскольку компилятор просто добавит его как новое свойство без предупреждения:

 interface UncaughtIntentColors extends Record<Intent, Color> {
  primairy: '#fff' | 'white';
  secondery: '#000' | 'black';
  erorr: '#ff0000' | 'pink';
} // no errors
  

Вам решать, стоит ли краткость расширения интерфейса этого возможного момента преткновения.


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

 type Extends<T, U extends T> = void;
  

Extends Тип не вычисляется ни для чего интересного (это всегда void так), но если вы напишете Extends<A, B> , то появится ошибка, если B не присваивается A .

Таким образом, вы можете сначала определить, IntentColors не упоминая Record<Intent, Color> :

 type IntentColors = {
  primary: '#fff' | 'white';
  secondary: '#000' | 'black';
  error: '#ff0000' | 'pink';
}
  

а затем проверьте это с помощью Extends :

 type TestIntentColors = Extends<Record<Intent, Color>, IntentColors>; // no error
  

Если вы допустите какую-либо ошибку:

 type BadIntentColors = {
  primary: '#fff' | 'white';
  secondery: '#000' | 'black'; // oops
  error: '#ff0000' | 'pink';
}
  

Это должно быть перехвачено:

 type TestBadIntentColors = Extends<Record<Intent, Color>, BadIntentColors>; // error!
// -----------------------------------------------------> ~~~~~~~~~~~~~~~
//  Property 'secondary' is missing in type 'BadIntentColors' 
// but required in type 'Record<Intent, string>'.
  

Это может быть ближе к тому поведению, которое вы ищете, но опять же, вам решать, стоит ли такое поведение подробного добавления псевдонима вспомогательного типа и отдельной строки кода «проверить».


Хорошо, надеюсь, что одно из этих действий поможет. Удачи!

Игровая площадка ссылка на код