重写 Typescript d.ts 文件中定义的接口 属性 类型
Overriding interface property type defined in Typescript d.ts file
有没有办法更改打字稿 *.d.ts
中定义的接口类型 属性?
例如:
x.d.ts
中的接口定义为
interface A {
property: number;
}
我想在我写入的打字稿文件中更改它
interface A {
property: Object;
}
甚至这样也行得通
interface B extends A {
property: Object;
}
这种方法行得通吗?当我在我的系统上尝试时它不起作用。只是想确认这是否可能?
您无法更改现有 属性 的类型。
您可以添加一个属性:
interface A {
newProperty: any;
}
但改变现有类型:
interface A {
property: any;
}
结果出错:
Subsequent variable declarations must have the same type. Variable
'property' must be of type 'number', but here has type 'any'
您当然可以拥有自己的界面来扩展现有界面。在这种情况下,您只能将类型覆盖为兼容类型,例如:
interface A {
x: string | number;
}
interface B extends A {
x: number;
}
顺便说一下,您可能应该避免使用 Object
作为类型,而是使用 any
.
类型
在 docs for the any
type 中指出:
The any type is a powerful way to work with existing JavaScript,
allowing you to gradually opt-in and opt-out of type-checking during
compilation. You might expect Object to play a similar role, as it
does in other languages. But variables of type Object only allow you
to assign any value to them - you can’t call arbitrary methods on
them, even ones that actually exist:
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'.
有趣的是,我花了一天时间调查解决同一案件的可能性。
我发现这样做是不可能的:
// 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 个,以避免愚蠢的情况
我用的是先过滤字段再合并的方法
引用
interface A {
x: string
}
export type B = Omit<A, 'x'> & { x: number };
接口:
interface A {
x: string
}
interface B extends Omit<A, 'x'> {
x: number
}
稍微扩展@zSkycat 的答案,您可以创建一个接受两种对象类型的泛型和 returns 一个合并类型,其中第二个对象的成员覆盖第一个对象的成员。
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;
type ModifiedType = Modify<OriginalType, {
a: number;
b: number;
}>
interface ModifiedInterface extends Modify<OriginalType, {
a: number;
b: number;
}> {}
受 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; }
v2.0深度修改
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> {}
// ModifiedType =
{
a: {
b: string
d: {
e: number
f: number
}
}
b: {
c: number
}
f: number
}
查找 ModifyDeep
.
如果其他人需要通用实用程序类型来执行此操作,我提出了以下解决方案:
/**
* 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
准备好,请参阅 。
为了缩小 属性 的类型,简单的 extend
效果完美,如 :
interface A {
x: string | number;
}
interface B extends A {
x: number;
}
为了扩大,或者通常覆盖类型,你可以做:
interface A {
x: string
}
export type B = Omit<A, 'x'> & { x: number };
但是,如果您的接口 A
正在扩展通用接口,您将在使用 Omit
时丢失 A
剩余属性的自定义类型。
例如
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!
Omit
属性 扩展接口时:
interface A {
a: number;
b: number;
}
interface B extends Omit<A, 'a'> {
a: boolean;
}
注意:不确定我在这个答案中使用的语法在编写旧答案时是否可用,但我认为这是一个关于如何解决此问题中提到的示例的更好方法。
我遇到了一些与此主题相关的问题(覆盖界面属性),这就是我的处理方式:
- 首先创建一个通用接口,其中包含您想要使用的可能类型。
您甚至可以为通用参数选择一个 default
值,如您在 <T extends number | SOME_OBJECT = number>
中所见
type SOME_OBJECT = { foo: "bar" }
interface INTERFACE_A <T extends number | SOME_OBJECT = number> {
property: T;
}
- 然后您可以通过将值传递给通用参数(或省略它并使用默认值)来基于该协定创建新类型:
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"
}
}
像我这样懒人的简答:
type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> };
interface Overrided extends Omit<YourInterface, 'overrideField'> {
overrideField: <type>
}
我创建了这种允许我轻松覆盖嵌套接口的类型:
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> {}
// ModifiedType =
{
a: {
b: string
d: {
e: number
f: number
}
}
b: {
c: number
}
f: number
}
日期:2021 年 3 月 19 日。
我认为最新的 typescript(4.1.2) 版本在 d.ts
文件中支持 interface
覆盖。
// 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" }
如果您只想修改现有 属性 的类型而不删除它,那么 & 就足够了:
// 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}
有没有办法更改打字稿 *.d.ts
中定义的接口类型 属性?
例如:
x.d.ts
中的接口定义为
interface A {
property: number;
}
我想在我写入的打字稿文件中更改它
interface A {
property: Object;
}
甚至这样也行得通
interface B extends A {
property: Object;
}
这种方法行得通吗?当我在我的系统上尝试时它不起作用。只是想确认这是否可能?
您无法更改现有 属性 的类型。
您可以添加一个属性:
interface A {
newProperty: any;
}
但改变现有类型:
interface A {
property: any;
}
结果出错:
Subsequent variable declarations must have the same type. Variable 'property' must be of type 'number', but here has type 'any'
您当然可以拥有自己的界面来扩展现有界面。在这种情况下,您只能将类型覆盖为兼容类型,例如:
interface A {
x: string | number;
}
interface B extends A {
x: number;
}
顺便说一下,您可能应该避免使用 Object
作为类型,而是使用 any
.
在 docs for the any
type 中指出:
The any type is a powerful way to work with existing JavaScript, allowing you to gradually opt-in and opt-out of type-checking during compilation. You might expect Object to play a similar role, as it does in other languages. But variables of type Object only allow you to assign any value to them - you can’t call arbitrary methods on them, even ones that actually exist:
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'.
有趣的是,我花了一天时间调查解决同一案件的可能性。 我发现这样做是不可能的:
// 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 个,以避免愚蠢的情况
我用的是先过滤字段再合并的方法
引用
interface A {
x: string
}
export type B = Omit<A, 'x'> & { x: number };
接口:
interface A {
x: string
}
interface B extends Omit<A, 'x'> {
x: number
}
稍微扩展@zSkycat 的答案,您可以创建一个接受两种对象类型的泛型和 returns 一个合并类型,其中第二个对象的成员覆盖第一个对象的成员。
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;
type ModifiedType = Modify<OriginalType, {
a: number;
b: number;
}>
interface ModifiedInterface extends Modify<OriginalType, {
a: number;
b: number;
}> {}
受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; }
v2.0深度修改
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> {}
// ModifiedType =
{
a: {
b: string
d: {
e: number
f: number
}
}
b: {
c: number
}
f: number
}
查找 ModifyDeep
如果其他人需要通用实用程序类型来执行此操作,我提出了以下解决方案:
/**
* 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
准备好,请参阅
为了缩小 属性 的类型,简单的 extend
效果完美,如
interface A {
x: string | number;
}
interface B extends A {
x: number;
}
为了扩大,或者通常覆盖类型,你可以做
interface A {
x: string
}
export type B = Omit<A, 'x'> & { x: number };
但是,如果您的接口 A
正在扩展通用接口,您将在使用 Omit
时丢失 A
剩余属性的自定义类型。
例如
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!
Omit
属性 扩展接口时:
interface A {
a: number;
b: number;
}
interface B extends Omit<A, 'a'> {
a: boolean;
}
注意:不确定我在这个答案中使用的语法在编写旧答案时是否可用,但我认为这是一个关于如何解决此问题中提到的示例的更好方法。
我遇到了一些与此主题相关的问题(覆盖界面属性),这就是我的处理方式:
- 首先创建一个通用接口,其中包含您想要使用的可能类型。
您甚至可以为通用参数选择一个 default
值,如您在 <T extends number | SOME_OBJECT = number>
type SOME_OBJECT = { foo: "bar" }
interface INTERFACE_A <T extends number | SOME_OBJECT = number> {
property: T;
}
- 然后您可以通过将值传递给通用参数(或省略它并使用默认值)来基于该协定创建新类型:
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"
}
}
像我这样懒人的简答:
type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> };
interface Overrided extends Omit<YourInterface, 'overrideField'> {
overrideField: <type>
}
我创建了这种允许我轻松覆盖嵌套接口的类型:
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> {}
// ModifiedType =
{
a: {
b: string
d: {
e: number
f: number
}
}
b: {
c: number
}
f: number
}
日期:2021 年 3 月 19 日。
我认为最新的 typescript(4.1.2) 版本在 d.ts
文件中支持 interface
覆盖。
// 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" }
如果您只想修改现有 属性 的类型而不删除它,那么 & 就足够了:
// 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}