TypeScript:区分联合与元组中的值

时间:2018-10-30 21:18:42

标签: typescript discriminated-union

是否可以基于元组属性中的第一个元素来区分联合类型?

例如

type Landing = {
  tokens: ['landing']
};

type Month = {
  tokens: ['month', string]
};

type Day = {
  tokens: ['day', string, string]
};

type Route =
  | Month
  | Day
  | Landing;

let route: Route = getRoute();

if(route.tokens[0] === 'day') {
  // resolve to Day type
}

更新: 如果没有直接的方法可以做到,那么我将对自定义类型防护感到满意,但还无法使它工作。为了明确起见,我想要一个可以区别联合而不是对每个变体进行显式检查的类型保护器。

例如

if(typeGuard(route, 'day')) {
    // compiler knows this id Day type
}
else if(typeGuard(route, 'month')) {
    // compiler knows this is Month type
}

2 个答案:

答案 0 :(得分:3)

修改 自从原始答案以来,打字稿在区分工会方面就变得更好了,因此这在打字稿3.3上可以正常使用:

if (route.tokens[0] === "day") {
    // resolve to Day type
    route.tokens[0] === 'day'
} else if (route.tokens[0] === "landing") {
    // resolve to Landing type
    route.tokens[0] === 'landing'
} else {
    // resolve to Month type
    route.tokens[0] === 'month'
}

原始

虽然为每个工会成员写一个类型保护的解决方案是一个非常有效的解决方案,但是如果工会的成员很多,或者如果您以后在工会中添加额外的成员,它的确会造成问题。

您可以使用Extract条件类型创建自定义类型防护,以保护所有可能的类型:

type Landing = {
    tokens: ['landing']
};

type Month = {
    tokens: ['month', string]
};

type Day = {
    tokens: ['day', string, string]
};

type Route =
    | Month
    | Day
    | Landing;

declare let route: Route;

function isRoute<T extends Route['tokens'][0]>(r: Route, type: T): r is Extract<Route, { tokens: [T, ...any[]] }> {
    return route.tokens[0] === type;
}

if (isRoute(route, 'day')) {
    // resolve to Day type
    route.tokens[0] === 'day'
} else if (isRoute(route, 'landing')) {
    // resolve to Landing type
    route.tokens[0] === 'landing'
} else {
    // resolve to Month type
    route.tokens[0] === 'month'
}

Playground link

答案 1 :(得分:1)

使用typeguard,就像这样:

type Landing = {
  tokens: ['landing']
};
const isLanding = (x: any): x is Landing => x[0] === 'landing'

type Month = {
  tokens: ['month', string]
};
const isMonth = (x: any): x is Month => x[0] === 'month'

type Day = {
  tokens: ['day', string, string]
};
const isDay = (x: any): x is Day => x[0] === 'day'

type Route =
  | Month
  | Day
  | Landing;

let route: Route = getRoute();

if(isDay (route)) {
  // resolve to Day type
}

您可以在official documentation(搜索类型防护)中找到有关类型防护的更多信息。

希望它能回答您的问题;)