Как можно преобразовать вложенный интерфейс в типовой псевдоним в TypeScript?

Вопросы и ответы

Я пытаюсь присвоить тип вложенного интерфейса (из сторонней библиотеки, которую я не могу изменить) типу с совместимой индексной сигнатурой.

Один из способов сделать такое присвоение работающим – это сначала преобразовать интерфейс в псевдоним типа (ссылка на 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;

Пояснение:

  1. Индексы: Мы создали тип ConvertToIndexSignature, который рекурсивно обходит все свойства объекта T. Если свойство является объектом, он снова применяет ConvertToIndexSignature, в противном случае возвращает его исходный тип.

  2. Псевдоним типа: Затем мы создаем тип TObj, который применяет ConvertToIndexSignature к IObj. Это позволяет преобразовать вложенные интерфейсы InnerOne и InnerTwo в тип, совместимый с индексной сигнатурой.

  3. Присвоение: Теперь объект objAlias имеет тип TObj, и его можно успешно присвоить типу IndexSignature | null.

Таким образом, данное решение позволяет вам работать с вложенными интерфейсами как с типами, совместимыми с индексными сигнатурами.

Оцените материал
Добавить комментарий

Капча загружается...