我知道在扩展接口中重写接口的属性、修改其类型是被禁止的。
我正在寻找一种替代解决方案,它允许我不复制第一个界面的内容(它相当大)。
下面是我的第一个天真的方法。鉴于该基本接口:
interface OrginalInterface {
title?: string;
text?: string;
anotherProperty?: SomeType;
// lots of other properties
}
该接口是在库中定义的。 我无法修改它(例如添加泛型)来满足我在扩展接口中的需求。
在包装器库(我的)使用的扩展接口中,我想重用现有接口,同时使某些字段具有不同的类型:
interface ExtendedInterface extends OriginalInterface {
title?: string | ReactElement<any>;
text?: string | ReactElement<any>;
}
但这不可能。
error TS2430: Interface 'ExtendedInterface' incorrectly extends interface 'OriginalInterface'.
Types of property 'title' are incompatible.
Type 'ReactElement<any>' is not assignable to type 'string'.
我还尝试将两个界面合并在一起:
type Extended = OriginalInterface & NewInterfaceWithOverridingPropertiesOnly;
虽然编译通过了,但是不起作用。如果您声明此类型的变量,则只能分配具有与
OriginalInterface
兼容结构的对象。
我觉得 TypeScript 的类型系统没有为我提供任何其他方式来表达我需要声明一个从 OrginalInterface
派生的新类型
。我不需要将新类型分配给
OriginalInterface
;我只需要它来重用
OriginalInterface
的大部分属性。我需要类似映射类型的东西,以及属性受到影响的条件。也许来自预发布 TypeScript 2.8 的
条件类型?还是应该复制第一个界面的内容?
Diff<T, U>
。 如果您使用的是 TypeScript 2.8 或更高版本,您绝对应该使用
Exclude<>
而不是
Diff<>
。此外,在 TypeScript 2.9 中,
keyof any
从
string
扩展到
string | number | symbol
,因此下面的
Diff<T, U>
会导致错误,可以通过将
Diff<T extends string, U extends string>
更改为
Diff<T extends keyof any, U extends keyof any>
来修复。 此更改已在下面进行。
条件类型将启用此功能,尽管您也可以在没有它们的情况下获得此行为。
这个想法是从原始界面中提取属性,然后用新的替换它们。 像这样:
type Diff<T extends keyof any, U extends keyof any> =
({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;
interface OriginalInterface {
title?: string;
text?: string;
anotherProperty?: SomeType;
// lots of other properties
}
interface Extension {
title?: string | ReactElement<any>;
text?: string | ReactElement<any>;
}
interface ExtendedInterface extends Overwrite<OriginalInterface, Extension> {};
const ext: ExtendedInterface = {}; // okay, no required properties
ext.text; // string | ReactElement<any> | undefined
ext.title; // string | ReactElement<any> | undefined
ext.anotherProperty; // SomeType | undefined
编辑:我更改了 Overwrite<T,U>
的定义,以尊重
T
中属性的可选/必需状态,其键不存在于
U
中。我认为这有你想要的行为。 希望有帮助;祝你好运!
省略:
省略通过从 T 中选取所有属性然后删除 K 来构造类型
// original interface
interface A {
a: number;
b: number; // we want string type instead of number
}
// Remove 'b'
type BTemp = Omit<A, 'b'>;
// extends A (BTemp) and redefine b
interface B extends BTemp {
b: string;
}
const a: B = {
a: 5,
b: 'B'
}
type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> };
通过数据库接收数据时,日期字段是
Date
对象,但当由 REST API 使用时,日期作为字符串对象(ISO 日期)。由于为两者提供单独的接口定义是不现实的(这会违背拥有公共库的目的),因此我在公共库中定义类型如下:
type IsoDateString = string
type DatesAsIsoString<T, U extends keyof T> = Omit<T, U> & { [key in U]: IsoDateString }
// Example object
interface User {
id: number
registeredAt: Date
}
// on the back end
const user: User = await getUserFromDatabase()
console.log(user.registeredAt) // Date
user.registeredAt.getTime() // ms epoch
// on the front end, define
type FetchedUser = DatesAsIsoString<User, 'registeredAt'>
// then use
const user: FetchedUser = await getUserFromApi()
console.log(user.registeredAt) // string, e.g. 2020-08-25T05:52:03.000Z
const actualDate = new Date(user.registeredAt) // date object
Omit<YourInterface, 'propertyA' | 'propertyB'>
:
export interface ErrorResponseSanitizedInterface extends Omit<
ErrorResponseInterface, 'error' | 'data'
> {
error: string | SanitizedMessage.DEFAULT | ErrorDataInterface['error']
data: SanitizedMessage.DEFAULT | ErrorDataInterface['data']
stack: ErrorResponseFullInterface['stack']
}