让我们考虑以下代码。我有一个ADT,其中具体类型之间的差异是kind
属性。我想有一个实用程序方法来创建这些类的实例。
interface I {
id: number;
}
interface A extends I {
kind: "a";
}
interface B extends I {
kind: "b";
}
type O = A | B;
function create(id: number, kind: "a" | "b"): O {
return {
id: 1,
kind: kind
};
}
显然上面的代码会出错,因为"a" | "b"
无法分配给"a"
或"b"
。反正有没有表达这个功能?以某种方式表达类型oneOf "a" | "b"
?
答案 0 :(得分:2)
更新:2019-05-30随着TypeScript 3.5的发布,这应由smarter union type checking解决。以下内容适用于3.4及以下:
我认为您已正确表达了代码,而且TypeScript阻止了有效的分配。你和我很明显O
应该在结构上与
type OPrime = {
id: number;
kind: "a" | "b"
}
由于create()
返回OPrime
类型的值,因此应将其分配给O
。唉,TypeScript认为O
可分配给OPrime
,但反之亦然。
最简单的解决方法是使用类型断言:
function create(id: number, kind: "a" | "b"): O {
return {
id: 1,
kind: kind
} as O; // assertion
}
类型断言通常不安全,但是当您知道的不仅仅是编译器可以弄清楚表达式的类型时,它们也很有用。但是很难区分编译器警告你的合法错误,以及编译器无法识别有效表达式。在这种情况下,我很确定你做对了,编译器报告错误的误报。
对于TypeScript,结果是intended behavior。虽然O
和OPrime
恰好相同,但编译器需要额外的工作来识别它。并且通常这是not warranted,因为两个联合成分很少仅仅通过单个同名属性的类型来区分。在大多数情况下(甚至可能是您的实际非玩具用例),您的A
和B
类型中会包含其他类型。例如:
interface AExtra extends I {
kind: "a";
name: string;
}
interface BExtra extends I {
kind: "b";
age: number;
}
type OExtra = AExtra | BExtra;
现在不再可能将OExtra
表示为具有联合值属性的单个非联合类型。考虑OExtraPrime
:
type OExtraPrime = {
id: number;
kind: "a" | "b";
name?: string;
age?: number;
}
确实,任何OExtra
都可以分配给OExtraPrime
,但反之亦然。因此,在一般实践中,O
和OPrime
之间的等价性没有出现,编译器也不愿意建立它。所以你能做的最好的就是类型断言。
希望这会有所帮助;祝你好运!
答案 1 :(得分:0)
可以使用overload declarations键入此类函数。您可以将所需类型作为重载之一,其他人在静态知道kind
参数时声明更具体的返回类型。重载函数实现中的实际返回类型更少(它仅用于检查实现,而不是调用站点),因此它可以只是any
,或者它可以是与实现兼容的任何方便类型和所有重载声明。
interface I {
id: number;
}
interface A extends I {
kind: "a";
}
interface B extends I {
kind: "b";
}
type O = A | B;
function create(id: number, kind: "a"): A;
function create(id: number, kind: "b"): B;
function create(id: number, kind: O["kind"]): O;
function create<K extends O["kind"]>(id: number, kind: K): { id: number; kind: K } {
return {
id,
kind
};
}
function testA(id: number) { // inferred as function testA(id: number): A
return create(id, "a");
}
function test(id: number, kind: "a" | "b") {
// inferred as function test(id: number, kind: "a" | "b"): O
return create(id, kind);
}