覆盖Typescript d.ts文件中定义的覆盖接口属性类型

时间:2016-12-22 14:18:15

标签: javascript typescript typescript-typings

有没有办法改变打字稿中*.d.ts中定义的接口属性的类型?

例如: x.d.ts中的接口定义为

interface A {
  property: number;
}

我想在我写给

的打字稿文件中更改它
interface A {
  property: Object;
}

甚至可以这样做

interface B extends A {
  property: Object;
}

这种方法有效吗?当我尝试使用我的系统时,它无法正常工作。只是想确认它是否可能?

13 个答案:

答案 0 :(得分:26)

我使用的方法是先过滤字段,然后将它们组合起来。

参考Exclude property from type

interface A {
    x: string
}

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

对于界面:

interface A {
    x: string
}

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

答案 1 :(得分:22)

您无法更改现有属性的类型。

您可以添加属性:

interface A {
    newProperty: any;
}

但改变现有类型:

interface A {
    property: any;
}

导致错误:

  

后续变量声明必须具有相同的类型。变量   'property'必须是'number'类型,但这里有'any'类型

您当然可以拥有自己的界面,扩展现有界面。在这种情况下,您只能将类型覆盖为兼容类型,例如:

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

顺便说一下,您可能应该避免使用Object作为类型,而是使用any类型。

docs for the any type中声明:

  

任何类型都是使用现有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 :(得分:16)

稍微扩展@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;

答案 3 :(得分:13)

Omit扩展接口时的属性:

interface A {
  a: number;
  b: number;
}

interface B extends Omit<A, 'a'> {
  a: boolean;
}

答案 4 :(得分:10)

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

ZSkycat's extends Omit解决方案的启发,我想到了这一点:

type Modify<T, R> = Pick<T, Exclude<keyof T, keyof R>> & R

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

type B = Modify<A, {
  a: number;
  b: number;
}>
// { a: number; b: number; c: number; }

要逐步进行:

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

答案 5 :(得分:3)

有趣的是,我花了一天时间研究解决同一案件的可能性。 我发现不可能这样做:

// 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,以避免愚蠢的情况

答案 6 :(得分:3)

为缩小属性的类型,简单的extend就像Nitzan's answer一样完美:

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

要扩展类型或通常覆盖类型,可以执行Zskycat's solution

interface A {
    x: string
}

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

但是,如果您的界面A扩展了通用界面,则使用A时,您将丢失Omit其余属性的自定义类型。

例如

interface A extends Record<string | number, number | string | boolean> {
    x: string;
    y: boolean;
}

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

let b: B = { x: 2, y: "hi" }; // no error on b.y! 

原因是,Omit内部仅遍历Exclude<keyof A, 'x'>键,在我们的例子中将是常规的string | number。因此,B将成为{x: number; },并接受类型为number | string | boolean的任何其他属性。


要解决此问题,我提出了另一种OverrideProps实用程序类型,如下所示:

type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };

示例:

type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };

interface A extends Record<string | number, number | string | boolean> {
    x: string;
    y: boolean;
}

export type B = OverrideProps<A, { x: number }>;

let b: B = { x: 2, y: "hi" }; // error: b.y should be boolean!

答案 7 :(得分:3)

像我这样的懒人的简短答案:

type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> }; 

答案 8 :(得分:3)

我创建了这种类型,它允许我轻松覆盖嵌套接口:

"".join(str(random.randint(0, 1) if x.isdigit() else x for x in F)

然后你可以这样使用它:

type ModifyDeep<A extends AnyObject, B extends DeepPartialAny<A>> = {
  [K in keyof A]: B[K] extends never
    ? A[K]
    : B[K] extends AnyObject
      ? ModifyDeep<A[K], B[K]>
      : B[K]
} & (A extends AnyObject ? Omit<B, keyof A> : A)

/** Makes each property optional and turns each leaf property into any, allowing for type overrides by narrowing any. */
type DeepPartialAny<T> = {
  [P in keyof T]?: T[P] extends AnyObject ? DeepPartialAny<T[P]> : any
}

type AnyObject = Record<string, any>
interface Original {
  a: {
    b: string
    d: {
      e: string // <- will be changed
    }
  }
  f: number
}

interface Overrides {
  a: {
    d: {
      e: number
      f: number // <- new key
    }
  }
  b: {         // <- new key
    c: number
  }
}

type ModifiedType = ModifyDeep<Original, Overrides>
interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}

答案 9 :(得分:3)

日期:19/3/2021。 我认为最新的 typescript(4.1.2) 版本支持 interface 文件中的 d.ts 覆盖。

// in test.d.ts

interface A {
  a: string
}

export interface B extends A {
  a: number
}

// in any ts file
import { B } from 'test.d.ts'

// this will work
const test: B = { a: 3 }

// this will not work
const test1: B = { a: "3" }

答案 10 :(得分:1)

如果其他人需要通用实用程序类型来执行此操作,我想出了以下解决方案:

/**
 * Returns object T, but with T[K] overridden to type U.
 * @example
 * type MyObject = { a: number, b: string }
 * OverrideProperty<MyObject, "a", string> // returns { a: string, b: string }
 */
export type OverrideProperty<T, K extends keyof T, U> = Omit<T, K> & { [P in keyof Pick<T, K>]: U };

我需要这个,因为在我的情况下,覆盖的键本身就是一个泛型。

如果您还没有准备好Omit,请参阅Exclude property from type

答案 11 :(得分:0)

注意::不确定在编写较旧的答案时我在此答案中使用的语法是否可用,但我认为这是解决问题的一种更好的方法这个问题中提到的示例。


我遇到了与此主题相关的一些问题(覆盖接口属性),这就是我的处理方式:

  1. 首先创建一个通用接口,其中包含您要使用的可能类型。

您甚至可以使用为default中的通用参数选择一个<T extends number | SOME_OBJECT = number>

type SOME_OBJECT = { foo: "bar" }

interface INTERFACE_A <T extends number | SOME_OBJECT = number> {
  property: T;
}
  1. 然后,您可以根据该合同创建新类型,方法是将值传递给通用参数(或忽略它并使用默认值):
type A_NUMBER = INTERFACE_A;                   // USES THE default = number TYPE. SAME AS INTERFACE_A<number>
type A_SOME_OBJECT = INTERFACE_A<SOME_OBJECT>  // MAKES { property: SOME_OBJECT }

这是结果:

const aNumber: A_NUMBER = {
    property: 111  // THIS EXPECTS A NUMBER
}

const anObject: A_SOME_OBJECT = {
    property: {   // THIS EXPECTS SOME_OBJECT
        foo: "bar"
    }
}

Typescript playground

答案 12 :(得分:0)

如果你只想修改现有属性的类型而不是删除它,那么&就足够了:

// Style that accepts both number and percent(string)
type BoxStyle = {
  height?: string | number,
  width?: string | number,
  padding?: string | number,
  borderRadius?: string | number,
}

// These are both valid
const box1: BoxStyle = {height: '20%', width: '20%', padding: 0, borderRadius: 5}
const box2: BoxStyle = {height: 85, width: 85, padding: 0, borderRadius: 5}

// Override height and width to be only numbers
type BoxStyleNumeric = BoxStyle & {
  height?: number,
  width?: number,
}

// This is still valid
const box3: BoxStyleNumeric = {height: 85, width: 85, padding: 0, borderRadius: 5}

// This is not valid anymore
const box4: BoxStyleNumeric = {height: '20%', width: '20%', padding: 0, borderRadius: 5}