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


111

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

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

interface A {
  property: number;
}

我想在我写的打字稿文件中更改它

interface A {
  property: Object;
}

甚至这将工作

interface B extends A {
  property: Object;
}

这种方法行得通吗?当我在系统上尝试时,它不起作用。只想确认是否有可能?

Answers:


49

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

您可以添加一个属性:

interface A {
    newProperty: any;
}

但是更改现有的类型:

interface A {
    property: any;
}

导致错误:

后续变量声明必须具有相同的类型。变量“属性”的类型必须为“数字”,但此处的类型为“任何”

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

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

顺便说一句,您可能应该避免将其Object用作类型,而应使用type any

文档中,该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'.

使用Typescript> = 1.1通过扩展接口覆盖方法的类型时,您需要包括原始接口中的所有方法,否则您将收到错误消息,指出类型不兼容,请参见github.com/Microsoft/TypeScript/issues/978
jcubic

您可以先忽略要覆盖的值,然后重新定义它们,我们可以让@ZSkycat回答答案吗?
zeachco

Downvote将Java称为“其他语言”
wvdz

@wvdz并不是说我很在乎下降投票,但是您在说什么呢?有人在哪里提到Java?在页面上搜索“ java”只有一个发现,它就在您的评论中。
Nitzan Tomer

也许我有点脾气暴躁,但是这让我有点恼火,因为您只能说“ Java”,但您却说“其他语言”。还是真的有很多其他语言将Object作为通用基类?我知道C#,但是C#当然是受Java启发的。
wvdz

198

我使用一种方法,该方法首先过滤字段,然后将它们组合。

参考从类型中排除属性

interface A {
    x: string
}

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

用于界面:

interface A {
    x: string
}

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

2
很高兴知道这一点。但是问题是它仍然没有修改现有的。
Freewind

8
这正是我想要的。这是我没有料到的打字稿extend工作在默认情况下,但很可惜,这个小Omit补丁一切🙌
道森乙

1
扩展接口正是我想要的,谢谢!
mhodges

1
注意:您需要上面的打字稿3.5.3才能使用它。
威克森

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

interface ModifiedInterface extends Modify<OriginalType, {
  a: number;
  b: number;
}> {}

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

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

// before typescript@3.5
type Modify<T, R> = Pick<T, Exclude<keyof T, keyof R>> & R

例:

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

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

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

逐步进行:

type R0 = Omit<OriginalType, 'a' | 'b'>        // { c: number; }
type R1 = R0 & {a: number, b: number }         // { a: number; b: number; 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: number; c: number; }

TypeScript实用程序类型


8
这是一个很好的解决方案。
奥斯汀·布伦霍斯特

1
Noob在这里,但是您在示例中从接口更改为类型,不是吗?还是没有区别?
多米尼克

Niceeeeeeeeee:D
SaMiCoOo

1
@Dominic好点,我已经更新了答案。具有相同名称的两个接口可以合并。typescriptlang.org/docs/handbook/...
Qwerty全

33

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

1
非常酷:-)我之前已经使用Omit使用一个或两个属性来完成此操作,但这要酷得多:-)我经常想“扩展”服务器实体类型并更改客户端上某些必需或可选的东西。
Simon_Weaver

1
现在应该是公认的解决方案。“扩展”接口的最简洁方法。
manuhortet19年

7

Omit 扩展接口时的属性:

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

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

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标志禁用愚蠢的异常,告诉我们我们做错了什么。有趣的是,一切都按预期进行。

就我而言,我正在减小type的作用域视野x,它使我的代码更加严格。例如,您有100个属性的列表,并将其减少到10个,以避免出现愚蠢的情况


2

为了缩小属性的类型,简单的方法很extend完美,就像Nitzan的回答

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

要扩展或总体覆盖类型,可以执行Zskycat的解决方案

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!

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准备好,请参见从type中排除属性


1
这正是我在寻找的东西,谢谢您:D:D:D
dwoodwardgb

@dwoodwardgb很高兴它对其他人有用:-)
Toni
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.