#typescript
#typescript
Вопрос:
Я хотел бы иметь дело с такими данными, как
const stateTest = {
natures : {
nature1: {
amounts: {
column1: 10,
column2: 10,
},
natureDetails : {
detail1 : {
amounts: {
column1: 10,
column2: 10,
},
descriptionShown: false
}
}
}
}
}
где список столбцов (здесь column1,column2) является параметром
итак, я объявил следующие типы и интерфейсы
type State<T extends GenericColumnList> = T amp; {
natures: naturesSet<T>
}
type GenericColumnList = Record<string, number>
type naturesSet<T extends GenericColumnList> = Partial<Record<string, RowNature<T>>>
export interface RowNature<T extends GenericColumnList> {
natureDetails: NatureDetailsSet<T>
amounts: T
}
type NatureDetailsSet<T> = Partial<Record<string, RowDetailNature<T>>>
export interface RowDetailNature<T> {
amounts: T
descriptionShown: boolean
}
но когда я пытаюсь передать следующий интерфейс списка столбцов в качестве параметра для состояния
export interface MyColumns {
column1?: number
column2?: number
}
то есть :
const stateTest: State<MyColumns> = {
natures : {
nature1: {
amounts: {
column1: 10,
column2: 10,
},
natureDetails : {
detail1 : {
montants: {
column1: 10,
column2: 10,
},
descriptionShown: false
}
}
}
}
}
Компилятор typescript жалуется, что
‘Тип MyColumns не соответствует записи<string, number> ‘ и я не понимаю, почему.
Комментарии:
1. Предположительно
montants
, это опечатка?
Ответ №1:
Тип Record<string, number>
имеет подпись строкового индекса. interface
MyColumns
У него нет подписи индекса, и поэтому типы несовместимы.
Существует такая концепция, как неявная индексная подпись, в которой типы без явной индексной подписи рассматриваются как совместимые с индексируемым типом, если все известные свойства соответствуют индексной подписи… но это не относится к типам, объявленным как an interface
; это работает только с анонимными типами (или type
псевдонимами таких анонимных типов). В GitHub, microsoft / TypeScript # 15300, обсуждается открытая проблема. Оказывается, что, во всяком случае, на данный момент, такое поведение является преднамеренным.
Итак, один из способов справиться с этим — создать MyColumns
псевдоним типа анонимного типа вместо любого интерфейса, например:
export type MyColumns = {
column1?: number
column2?: number
}
Затем вам нужно будет включить undefined
в домен GenericColumnList
, потому что тип MyColumns['column1']
is number | undefined
, а не только number
(при условии, что мы используем рекомендуемые --strict
параметры компилятора, в том числе --strictNullChecks
):
type GenericColumnList = Record<string, number | undefined>
И тогда ваше назначение работает:
const stateTest: State<MyColumns> = {
natures: {
nature1: {
amounts: {
column1: 10,
column2: 10,
},
natureDetails: {
detail1: {
amounts: {
column1: 10,
column2: 10,
},
descriptionShown: false
}
}
}
}
}; // okay
Предполагая, что мы не хотим требовать, чтобы люди использовали не- interface
типы для T
, мы могли бы вместо этого сделать GenericColumnList
его универсальным, T
чтобы вместо подписи индекса он имел те же ключи, что и T
:
type GenericColumnList<T> = { [K in keyof T]?: number }
Здесь мы сделали свойства необязательными ( ?
) по той же причине, что и при добавлении | undefined
ранее: чтобы сделать необязательные / отсутствующие ключи совместимыми при наличии --strictNullChecks
.
Это требует некоторого разбрызгивания <T>
вашего кода:
type State<T extends GenericColumnList<T>> = T amp; {
natures: NaturesSet<T>
}
type NaturesSet<T extends GenericColumnList<T>> = Partial<Record<string, RowNature<T>>>
export interface RowNature<T extends GenericColumnList<T>> {
natureDetails: NatureDetailsSet<T>
amounts: T
}
и снова, ваше назначение будет работать:
const stateTest: State<MyColumns> = {
natures: {
nature1: {
amounts: {
column1: 10,
column2: 10,
},
natureDetails: {
detail1: {
amounts: {
column1: 10,
column2: 10,
},
descriptionShown: false
}
}
}
}
}; // okay