首页 > 解决方案 > 在 Typescript 中,如何确保变量匹配联合类型的情况而不重写其定义?

问题描述

我有一个这样的联合类型:

type Union
    = { kind: UnionKind.One, x: string }
    | { kind: UnionKind.Two, y: number }

enum UnionKind { One, Two }

我想创建一个返回第一种情况的构造函数。

function one(x: string): ReturnType {
    return { kind: UnionKind.One, x: x }
}

我可以指定ReturnType为 (1){ kind: UnionKind.One, x: string }或 (2) Union

(1) 有一个缺点,如果我将来更改类型,该函数仍然会成功编译:

type Union
    = { kind: UnionKind.One, somethingElse: string }
    | { kind: UnionKind.Two, y: number }

// unfortunately, compiles :(
function one(x: string): { kind: UnionKind.One, x: string } {
    return { kind: UnionKind.One, x: x }
}

(2) 的缺点是它会丢失我确定的信息并通过,即使我返回UnionKind.Two.

function one(x: string): Union {
    return { kind: UnionKind.One, x: x }
}
const foo = one("foo"); // foo is Union although I'm sure it's of UnionKind.One
console.log(foo.x);  // compilation error!

// passes although I want to return it something of `one`
function one(x: string): Union {
    return { kind: UnionKind.Two, y: x.toString() }
}

好像我想说:

function one(x: string): returns conforms both to { kind: UnionKind.One, x: string } and Union {
    return { kind: UnionKind.One, x: x }
}

那么,怎么说呢?另外,如果我不必重复案例的类型定义会很好。

注意:我并没有以特殊的方式使用构造函数这个词,我的意思是它是一个通用函数。

标签: typescript

解决方案


使用{ kind: UnionKind.One } & Union. 优点:

// (1) You don't have to write the whole definition of the case
function one(x: string): { kind: UnionKind.One } & Union {
    return { kind: UnionKind.One, x: x }
}

// (2) It infers it's of the first case
const foo = one("foo");
console.log(foo.x); // Works! :)

// (3) If you return something not of `One`, it'll tell:
// happily doesn't compile :-)
function one(x: string): { kind: UnionKind.One } & Union {
    return { kind: UnionKind.Two, y: x.toString() }
}

// (4) If you change the union type in the future, you'll get a compilation error :)
type Union
    = { kind: UnionKind.One, somethingElse: string }
    | { kind: UnionKind.Two, y: number }

// Error! :)
function one(x: string): { kind: UnionKind.One } & Union {
    return { kind: UnionKind.One, x: x }
}

再次注意:您不需要重复案例的整个定义!


推荐阅读