Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/450.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript TypeScript类工厂需要一个交集_Javascript_Typescript - Fatal编程技术网

Javascript TypeScript类工厂需要一个交集

Javascript TypeScript类工厂需要一个交集,javascript,typescript,Javascript,Typescript,我有下面的类工厂pickSomething,它基于从ClassMap传入的键创建一个类型: class A { keya = "a" as const; } class B { keyb = "b" as const; } type ClassMap = { a: A b: B } const pickSomething = <K extends keyof ClassMap>(key: K): ClassMap[K] =

我有下面的类工厂
pickSomething
,它基于从
ClassMap
传入的键创建一个类型:

class A {
  keya = "a" as const;
}
class B {
  keyb = "b" as const;
}

type ClassMap = {
  a: A
  b: B
}


const pickSomething = <K extends keyof ClassMap>(key: K): ClassMap[K] => {
  switch (key) {
    case 'a':
      return new A(); // Error: A is not assignable to A & B
    case 'b':
      return new B(); // Error: B is not assignable to A & B
  }
  throw new Error();
}

// It works fine externally
const a = pickSomething('a').keya;
const b = pickSomething('b').keyb;
A类{
keya=“a”作为常量;
}
B类{
keyb=“b”作为常量;
}
类型类映射={
a:a
b:b
}
常量pickSomething=(键:K):类映射[K]=>{
开关(钥匙){
案例“a”:
返回新的A();//错误:A不能分配给A&B
案例“b”:
return new B();//错误:B不能分配给A&B
}
抛出新错误();
}
//它的外观很好
常数a=pickSomething('a')。keya;
常数b=pickSomething('b').keyb;

它在外部工作正常(正如您从
const a=pickSomething('a').keya;
中看到的)。这意味着外部
ClassMap[K]
正在映射到正确的实例(
A
B
,具体取决于传入的
)。但是在内部,我在每个return语句上都会得到一个错误。TypeScript期望
ClassMap[K]
表示
A&B
。有没有更好的类型注释(不诉诸类型断言)来解决这个问题?

我认为一般的问题是,TypeScript不会像通常对特定联合类型的值那样,通过控制流分析来缩小扩展联合的类型参数。请参阅以进行讨论。您已经检查了
“a”
还是
“b”
,并且
的类型是
K
,但这对
K
本身没有影响。由于编译器不知道
K
“a”|“b”
窄,所以它不知道
ClassMap[K]
可以比
a&b
宽。(,写入键并集上的查找属性需要属性的交集;请参见。)

从技术上讲,编译器拒绝进行这种缩小是正确的,因为没有任何东西可以阻止类型参数
K
被指定为完全联合类型
“a”|“b”
,即使您检查它:

pickSomething(Math.random() < 0.5 ? "a" : "b"); // K is "a" | "b"
但产生的JavaScript至少是惯用的


其他可能性:编译器允许您通过在类型为
T
的对象上实际查找键类型为
K
的属性来返回查找属性类型
T[K]
。您可以重构代码来实现这一点:

const pickSomething = <K extends keyof ClassMap>(key: K): ClassMap[K] => {
    return {
        a: new A(),
        b: new B()
    }[key];
}
它编译时不会出错,并且是类型安全的。但这是奇怪的代码,所以我不知道是否值得。目前,我认为类型断言是正确的方法。希望在某个时候,会有一个更好的解决方案,使您的原始代码可以在不需要断言的情况下工作



如果你一定要问,可能没有。但我们可能会在TypeScript的问题追踪器中找到一些reportson…这不是另一个问题吗?TypeScript不知道
K
没有实例化为
'a'|'b'
。这种情况下
K
可能比单个键更宽。理论上,您可以调用
pickSomething('b')
,但返回类型无效。实际上,你不会这么做的。所以只要使用断言就行了。我想重载可能会有所帮助,但这不会使用
ClassMap
编写
as ClassMap[K]
as A&B
更能证明未来
const pickSomething = <K extends keyof ClassMap>(key: K): ClassMap[K] => {
    return {
        a: new A(),
        b: new B()
    }[key];
}
const pickSomething = <K extends keyof ClassMap>(key: K): ClassMap[K] => {
    return {
        get a() { return new A() },
        get b() { return new B() }
    }[key];
}