Переопределение типа свойства интерфейса, определенного в файле Typescript d.ts

Есть ли способ изменить тип свойства интерфейса, определенный в *.d.ts в машинописи?

например: интерфейс в xdts определяется как

interface A {
  property: number;
}

Я хочу изменить его в файлах машинописи, в которые я пишу

interface A {
  property: Object;
}

или даже это будет работать

interface B extends A {
  property: Object;
}

Будет ли этот подход работать? Это не сработало, когда я попробовал свою систему. Просто хотите подтвердить, если это вообще возможно?

Ответ 1

Вы не можете изменить тип существующего свойства.

Вы можете добавить свойство:

interface A {
    newProperty: any;
}

Но меняем тип существующего:

interface A {
    property: any;
}

Приводит к ошибке:

Последующие объявления переменных должны иметь одинаковый тип. Переменная 'property' должна иметь тип 'number', но здесь имеет тип 'any'

Конечно, вы можете иметь свой собственный интерфейс, который расширяет существующий. В этом случае вы можете переопределить тип только на совместимый тип, например:

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

Кстати, вам, вероятно, следует избегать использования Object в качестве типа, вместо этого используйте тип any.

В документах для any типа говорится:

Любой тип - это мощный способ работы с существующим JavaScript, позволяющий постепенно включать и отключать проверку типов во время компиляции. Вы можете ожидать, что Object будет играть аналогичную роль, как и в других языках. Но переменные типа Object позволяют вам только присвоить им любое значение - вы не можете вызывать произвольные методы для них, даже те, которые действительно существуют:

let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

Ответ 2

Я использую метод, который сначала фильтрует поля, а затем объединяет их.

ссылка Исключить свойство из типа

interface A {
    x: string
}

export type B = Omit<A, 'x'> & { x: number };

для интерфейса:

interface A {
    x: string
}

interface B extends Omit<A, 'x'> {
  x: number
}

Ответ 3

type ModifiedType = Modify<OriginalType, {
  a: number;
  b: number;
}>

Вдохновленный решением ZSkycat extends Omit, я придумал следующее:

type Modify<T, R> = Omit<T, keyof R> & R;

// before [email protected]
type Modify<T, R> = Pick<T, Exclude<keyof T, keyof R>> & R

Пример:

interface OriginalType {
  a: string;
  b: boolean;
  c: number;
}

type ModifiedType  = Modify<OriginalType , {
  a: number;
  b: number;
}>

// ModifiedType = { a: number; b: number; c: number; }

Шаг за шагом:

type R0 = Omit<T, 'a' | 'b'>                   // { c: number; }
type R1 = R0 & {a: number, b: number }         // { a: number; b: boolean; c: number; }

type T0 = Exclude<'a' | 'b' | 'c' , 'a' | 'b'> // 'c'
type T1 = Pick<OriginalType, T0>               // { c: number; }
type T2 = T1 & {a: number, b: number }         // { a: number; b: boolean; c: number; }

Ответ 4

Немного расширив ответ @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;
}

// redefine name to be string | number
type B = Merge<A, {
    name: string | number;
    favorite?: boolean;
}>;

let one: A = {
    name: 'asdf',
    color: 'blue'
};

// A can become B because the types are all compatible
let two: B = one;

let three: B = {
    name: 1
};

three.name = 'Bee';
three.favorite = true;
three.color = 'green';

// B cannot become A because the type of name (string | number) isn't compatible
// with A even though the value is a string
// Error: Type {...} is not assignable to type A
let four: A = three;

Ответ 5

Смешно, что я провожу день, расследуя возможность решить один и тот же случай. Я обнаружил, что это невозможно сделать так:

// a.ts - module
export interface A {
    x: string | any;
}

// b.ts - module
import {A} from './a';

type SomeOtherType = {
  coolStuff: number
}

interface B extends A {
    x: SomeOtherType;
}

Причина. Модуль может не знать обо всех доступных типах приложения. И это довольно скучный порт из всех, и делает такой код.

export interface A {
    x: A | B | C | D ... Million Types Later
}

Вы должны определить тип позже, чтобы автозаполнение работало хорошо.


Таким образом, вы можете немного обмануть:

// a.ts - module
export interface A {
    x: string;
}

По умолчанию оставлен некоторый тип, который позволяет выполнять автозаполнение, когда переопределения не требуются.

затем

// b.ts - module
import {A} from './a';

type SomeOtherType = {
  coolStuff: number
}

// @ts-ignore
interface B extends A {
    x: SomeOtherType;
}

Отключите глупое исключение, используя флаг @ts-ignore, говоря нам, что мы делаем что-то неправильно. И смешно, что все работает так, как ожидалось.

В моем случае я уменьшаю вид видимости вида x, это позволяет мне делать код более строгим. Например, у вас есть список из 100 свойств, и вы уменьшаете его до 10, чтобы избежать глупых ситуаций