这是我使用的API经常遇到的一种模式。我有一些对象,根据某种类型(在属性中描述),该对象将始终具有一些必需属性,某些属性始终是可选属性,而某些属性是某些特定类型时才需要(或完全存在)。我通常是这样解决问题的(也许用一些代码会更容易理解):
export type FoobarTypes = 'foo' | 'bar';
export interface FooBarBase {
id: string;
type: FoobarTypes;
optional?: any;
}
export interface FooBarFoo extends FooBarBase {
foo: any;
}
export interface FooBarBar extends FooBarBase {
bar: any;
}
export type FooBar = FooBarFoo | FooBarBar;
// and to differentiate between the types:
export const isFooBarFoo = (foobar: FooBar): foobar is FooBarFoo =>
(foobar as FooBarFoo).type === 'foo';
export const FooBarBar = (foobar: FooBar): foobar is FooBarBar =>
(foobar as FooBarBar).type === 'bar';
它工作得很好,但是我觉得有点复杂,应该有一个更好,更合适的方法来实现它。还是要走的路?
编辑:这只是@Fyodor接受的答案的进一步简化。我只是将其放在此处,因此,如果有人有相同的问题,则比在注释中更容易提及。他的答案仍然是正确的,如果不是他的话,我也不会同意。
export type FoobarTypes = 'foo' | 'bar';
// all of the shared properties go here
export interface FooBarBase {
id: string;
optional?: any;
}
// append the other, specific properties depending on type
export type FooBar<T extends FoobarTypes> =
T extends 'foo' ? FooBarBase & {
type: T;
foo: any;
} : T extends 'bar' ? FooBarBase & {
type: T;
bar: any;
} : never;
// and the usage...
function FB(fb: FooBar<FoobarTypes>) {
if (fb.type === 'foo') fb.foo = '1';
if (fb.type === 'bar') fb.bar = '2';
}
答案 0 :(得分:1)
您可以使用discriminated unions和泛型。这样可以根据type
属性类型使用完全不同的类型。
export type FoobarTypes = 'foo' | 'bar';
type FooBarBase<T extends FoobarTypes> =
T extends 'foo' ?
{
id: string;
type: T; // T is 'foo'
optional?: any;
foo: any;
} :
T extends 'bar' ?
{
id: string;
type: T; // T is 'bar'
optional?: any;
bar: any;
} : never;
type Foo = FooBarBase<'foo'>
type Bar = FooBarBase<'bar'>
function FB(fb: Foo | Bar) {
if (fb.type === 'foo') {
fb.foo = '1' // TS knows, that fb has foo prop and doesn't have bar
}
else
{
fb.bar = '2' // Here is opposite. fb only has 'bar'
}
}