Переопределение типа свойства интерфейса, определённого в файле d.ts TypeScript
Есть ли способ изменить тип свойства интерфейса, определенного в файле *.d.ts
в TypeScript?
Например, в файле x.d.ts
интерфейс определен так:
interface A {
property: number;
}
Я хочу изменить его в своих TypeScript файлах на:
interface A {
property: Object;
}
Или даже так:
interface B extends A {
property: Object;
}
Сработает ли такой подход? У меня не получилось этого сделать на моей системе. Хотелось бы подтвердить, возможно ли это вообще?
5 ответ(ов)
Краткий ответ для ленивых, как я:
type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <тип> };
interface Overrided extends Omit<YourInterface, 'overrideField'> {
overrideField: <тип>;
}
Эти примеры иллюстрируют, как создать новый тип или интерфейс, исключив поле overrideField
из YourInterface
и добавив его с новым типом.
Расширяя ответ @zSkycat, вы можете создать универсальный тип, который принимает два типа объектов и возвращает объединенный тип, где члены второго объекта переопределяют члены первого.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
interface A {
name: string;
color?: string;
}
// переопределяем имя так, чтобы это был string | number
type B = Merge<A, {
name: string | number;
favorite?: boolean;
}>;
let one: A = {
name: 'asdf',
color: 'blue'
};
// A может быть преобразован в B, так как типы совместимы
let two: B = one;
let three: B = {
name: 1
};
three.name = 'Bee';
three.favorite = true;
three.color = 'green';
// B не может быть преобразован в A, так как тип name (string | number) несовместим
// с A, даже если значение — это строка
// Ошибка: Type {...} is not assignable to type A
let four: A = three;
В этом примере у нас есть два типа: A
и B
, где тип B
объединяет свойства A
и добавляет или изменяет некоторые из них. Таким образом, вы можете использовать такой подход для более гибкого управления типами в TypeScript.
Чтобы исключить свойство из интерфейса при его расширении, можно воспользоваться утилитой TypeScript Omit
. В вашем примере вы хотите расширить интерфейс A
, исключив свойство a
, и добавить его с другим типом в интерфейс B
. Вот как это можно сделать:
interface A {
a: number;
b: number;
}
interface B extends Omit<A, 'a'> {
a: boolean;
}
В этом случае интерфейс B
наследует все свойства интерфейса A
, за исключением свойства a
, которое заменяется на свойство a
типа boolean
. Таким образом, в интерфейсе B
будет свойство b
типа number
и новое свойство a
типа boolean
.
Чтобы создать тип, который позволяет легко переопределять вложенные интерфейсы, я разработал следующий код:
export type DeepPartialAny<T> = {
[P in keyof T]?: T[P] extends Obj ? DeepPartialAny<T[P]> : any;
};
export type Override<A extends Obj, AOverride extends DeepPartialAny<A>> = { [K in keyof A]:
AOverride[K] extends never
? A[K]
: AOverride[K] extends Obj
? Override<A[K], AOverride[K]>
: AOverride[K]
};
Данный код можно использовать следующим образом:
interface Foo {
Bar: {
Baz: string;
};
}
type Foo2 = Override<Foo, { Bar: { Baz: number } }>;
const bar: Foo2['Bar']['Baz'] = 1; // number;
Таким образом, вы можете переопределять типы вложенных свойств в интерфейсах, сохраняя гибкость и типобезопасность.
Порой предложенные решения могут не сработать. Это может произойти в следующем случае.
interface A {
[k: string]: string;
}
interface B extends A {}
type C = Omit<B, 'x'> & { x: number }
В этом примере тип C будет иметь x со строковым типом в любом случае. Это может быть актуально, если вы хотите переопределить, например, тип process.env.
Чтобы исправить эту проблему, необходимо переопределить интерфейс A следующим образом, удаляя ключи более строго:
interface C {
x: number;
}
type ExcludeKeys<Type, Keys> = {
[Property in keyof Type as Exclude<Property, Keys>]: Type[Property];
};
type AA = ExcludeKeys<B, keyof C> & C;
Таким образом, с помощью утилиты типа ExcludeKeys мы можем более строго исключать нужные ключи и добиться желаемого результата в типе AA.
Как преобразовать строку в число в TypeScript?
Возможно ли расширять типы в TypeScript?
Когда использовать JSX.Element, ReactNode и ReactElement?
Параметр типа подписки индекса не может быть объединенным типом. Рассмотрите возможность использования типизированного объекта вместо этого!
Как проверить, содержит ли массив строку в TypeScript?