Я пытаюсь присвоить тип вложенного интерфейса (из сторонней библиотеки, которую я не могу изменить) типу с совместимой индексной сигнатурой.
Один из способов сделать такое присвоение работающим – это сначала преобразовать интерфейс в псевдоним типа (ссылка на https://github.com/Microsoft/TypeScript/issues/15300).
Как можно преобразовать вложенный интерфейс в псевдоним типа?
Ниже приведен пример (песочница).
interface InnerOne {
one: string;
two?: number;
}
interface InnerTwo {
innerTwoOne?: string;
innerTwoTwo?: string | null;
}
interface IObj {
one?: {
a?: "A" | "B" | undefined;
b?: Array<Partial<InnerOne>>;
c?: boolean;
} & InnerTwo;
three: string;
}
type IndexSignature = {
[key: string]:
| IndexSignature
| string
| boolean
| Array<IndexSignature>
| number
| null;
};
declare const obj: IObj | null;
type TObj = Pick<IObj, keyof IObj>; // Не сработало, так как вложенные интерфейсы InnerOne и InnerTwo остались интерфейсами.
const objAlias: TObj | null = obj;
const error: IndexSignature | null = objAlias;
Ответ
Чтобы преобразовать вложенный интерфейс в псевдоним типа в TypeScript, вы можете воспользоваться утилитным типом Pick
и рекурсивным типом. В вашем случае, вы можете создать тип, который будет рекурсивно проходить по всем вложенным интерфейсам и преобразовывать их в псевдонимы типа с совместимой индексной сигнатурой.
Вот пример того, как это можно сделать на основе вашего кода:
interface InnerOne {
one: string;
two?: number;
}
interface InnerTwo {
innerTwoOne?: string;
innerTwoTwo?: string | null;
}
interface IObj {
one?: {
a?: "A" | "B" | undefined;
b?: Array<Partial<InnerOne>>;
c?: boolean;
} & InnerTwo;
three: string;
}
// Рекурсивный тип для индексной сигнатуры
type IndexSignature = {
[key: string]:
| IndexSignature
| string
| boolean
| Array<IndexSignature>
| number
| null;
};
// Рекурсивный тип для конвертации интерфейсов в индексные сигнатуры
type ConvertToIndexSignature<T> = {
[K in keyof T]: T[K] extends object ? ConvertToIndexSignature<T[K]> : T[K];
};
declare const obj: IObj | null;
// Применяем рекурсивный тип, чтобы преобразовать IObj
type TObj = ConvertToIndexSignature<IObj>;
const objAlias: TObj | null = obj;
// Теперь можно присвоить его IndexSignature
const error: IndexSignature | null = objAlias;
Пояснение:
-
Индексы: Мы создали тип
ConvertToIndexSignature
, который рекурсивно обходит все свойства объектаT
. Если свойство является объектом, он снова применяетConvertToIndexSignature
, в противном случае возвращает его исходный тип. -
Псевдоним типа: Затем мы создаем тип
TObj
, который применяетConvertToIndexSignature
кIObj
. Это позволяет преобразовать вложенные интерфейсыInnerOne
иInnerTwo
в тип, совместимый с индексной сигнатурой. - Присвоение: Теперь объект
objAlias
имеет типTObj
, и его можно успешно присвоить типуIndexSignature | null
.
Таким образом, данное решение позволяет вам работать с вложенными интерфейсами как с типами, совместимыми с индексными сигнатурами.