#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>'.
Это может быть ближе к тому поведению, которое вы ищете, но опять же, вам решать, стоит ли такое поведение подробного добавления псевдонима вспомогательного типа и отдельной строки кода «проверить».
Хорошо, надеюсь, что одно из этих действий поможет. Удачи!