繁体   English   中英

TypeScript字符串联合不能分配给函数中的元组联合

[英]TypeScript union of strings not assignable to union of tuples in function

这两个示例的行为应相同,但第二个错误。 为什么?

// Example 1:
const a: 'x' | 'y' = 'x'; 
const b: ['x'] | ['y'] = [a]; // ok

// Example 2:
function fn(a: 'x' | 'y') {
  const b: ['x'] | ['y'] = [a];
  //    ^
  // Type '["x" | "y"]' is not assignable to type '["x"] | ["y"]'.
  //   Type '["x" | "y"]' is not assignable to type '["x"]'.
  //     Type '"x" | "y"' is not assignable to type '"x"'.
  //       Type '"y"' is not assignable to type '"x"'.
}

您可以在操场上尝试

更新:2019-05-30 TypeScript 3.5的发行版引入了更智能的联合类型检查 ,该检查针对对象类型(例如{a: "x"} | {a: "y"}进行了修复,但似乎并未做任何事情元组类型(例如["x"] | ["y"] )。不确定这是否是有意的。


在“示例1”中,将a初始化为"x"这一事实有很大的不同。 控制流分析变窄的类型a下降到只有"x"尽管你的注释为"x" | "y" "x" | "y"

let a: "x" | "y" = "x";
console.log(a === "y"); // error!
// This condition will always return 'false' 
// since the types '"x"' and '"y"' have no overlap.

因此,在这种情况下,当然[a]将匹配["x"] | ["y"] ["x"] | ["y"] ,因为编译器知道[a]的类型为["x"]


因此,示例1仅同时成功。 通常,这将失败。 编译器通常看不到[A] | [B] [A] | [B]等同于[A | B] [A | B] 前者被视为比后者严格狭窄的类型。

type Extends<T, U extends T> = true;
type OkayTup = Extends<[string | number], [string] | [number]>; 
type NotOkayTup = Extends<[string] | [number], [string | number]>; // error!

这可能令人惊讶,因为实际上[A | B] Type [A | B]应该可分配为类型[A] | [B] [A] | [B] 当您查看类似的属性袋版本时,也会发生同样的惊奇:

type OkayObj = Extends<{a: string | number}, {a: string} | {a: number}>;
type NotOkayObj = Extends<{a: string} | {a: number}, {a: string | number}>; // error!

同样, {a: A} | {a: B} {a: A} | {a: B}被视为比{a: A | B}严格更窄的类型{a: A | B} {a: A | B} ,尽管您很难为后一种类型分配一个值,但该值不能分配给前一种类型。

那么,这是怎么回事? 好吧,这似乎是TypeScript的故意设计限制 语言设计师的话说:

对于您的示例,如果没有错误地键入check,我们将不得不考虑以下形式的类型{ x: "foo" | "bar" } { x: "foo" | "bar" }等效于{ x: "foo" } | { x: "bar" } { x: "foo" } | { x: "bar" } 但是这种等价仅适用于具有单个属性的类型,在一般情况下并非如此。 例如,考虑{ x: "foo" | "bar", y: string | number } { x: "foo" | "bar", y: string | number } { x: "foo" | "bar", y: string | number }等于{ x: "foo", y: string } | { x: "bar", y: number } { x: "foo", y: string } | { x: "bar", y: number }因为第一种形式允许所有四种组合,而第二种形式仅允许两种特定的组合。

(请注意:等价关系在比上述情况略多的情况下适用...仅在每个联合组成中不同的属性在单属性情况下具有该联合的所有可能值的情况下才适用。因此, {x: string | number, y: boolean, z: string}等效于{x: string, y: true, z: string} | {x: string, y: false, z: string} | {x: number, y: true, z: string} | {x: number, y: false, z: string}

我想说这是一个设计局限性...检测相对较少的情况下,资产合并可以折叠/扩展将是非常昂贵的,而且实施起来不值得。


在实践中,如果发现自己遇到了合并属性合并问题,而编译器未进行验证但您知道它是安全的,请展示出卓越的才智并断言自己的出路:

function fn(a: 'x' | 'y') {
  const b = [a] as ['x'] | ['y'] // I'm smarter than the compiler 🤓 
}

好的,希望能有所帮助; 祝好运!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM