typescript 如果我添加了应该解析为给定类型的约束,为什么TS从不得到类型?

bzzcjhmw  于 2022-12-30  发布在  TypeScript
关注(0)|答案(1)|浏览(100)

我想知道如果我添加了一个应该保证期望类型的约束,为什么TypeScript会将条件类型解析为never

示例

假设我们已经编写了这样的代码

type PrimitiveDataType = string | number | bigint | boolean | symbol | undefined | null;

type ConditionalType<T> = T extends PrimitiveDataType
  ? (v: T) => void
  : never;

abstract class AbstractClass<T> {
  abstract value: T;
  protected conditionalFunctions: Map<ConditionalType<T>, number | undefined> = new Map();
}

class SomeClass<T extends PrimitiveDataType> extends AbstractClass<T> {
  value: T;

  constructor(value: T) {
    super();
    this.value = value;
  }

  someMethod() {
    for (const someFn of this.conditionalFunctions.keys()) {
      someFn(this.value);
    }
  }
}

在上面的代码中,我创建了一个PrimitiveDataType,它是JavaScript中所有原始数据类型的并集。
然后,我创建了一个ConditionalType<T>,只有当TPrimitiveDataType之一时,它才会解析为某个回调。
然后我创建了一个抽象泛型类,它有一个类型(ConditionalType<T>)依赖于该类泛型值的字段。
最后,我创建了一个SomeClass,它扩展了AbstractClass<T>,并添加了泛型参数T必须扩展PrimitiveDataType的约束,我得到了这个错误:

TS2345: Argument of type 'PrimitiveDataType' is not assignable to parameter of type 'never'.   
Type 'undefined' is not assignable to type 'never'.

结论

我想如果在SomeClassT中有一个约束,它必须是PrimitiveDataType上的一个,那么TypeScript将解析conditionalFunctions字段为Map<(v: T) => void, number | undefined>类型。令我惊讶的是,TypeScript将此类型解析为Map<(v: never) => void, number | undefined>,这对我来说是不清楚的,我不知道哪里是错误的方式,我该怎么想?
你能给我解释一下为什么会这样吗?或者可能是TypeScript编译器的bug?

观察

如果我只留下一个,键入PrimitiveDataType,那么一切正常,但对于多个,我得到一个错误

TypeScript游戏场链接

编辑1

如果我的条件类型有两个以上可能的返回类型,那么分布式[T]就不起作用,并给出这样的错误:

Expected 2 arguments, but got 1.

看起来TS现在把这个类型解析成了Array的Map类型,我现在完全不明白

    • 示例**
type PrimitiveDataType = string | number | bigint | boolean | symbol | undefined | null;

type ConditionalType<T> = [T] extends [PrimitiveDataType]
  ? (v: T) => void
  : T extends Array<unknown>
  ? (v: T, t: number) => void
  : never;

abstract class AbstractClass<T> {
  abstract value: T;
  protected conditionalFunctions: Map<ConditionalType<T>, number | undefined> = new Map();
}

class SomeClass<T extends PrimitiveDataType> extends AbstractClass<T> {
  value: T;

  constructor(value: T) {
    super();
    this.value = value;
  }

  someMethod() {
    for (const someFn of this.conditionalFunctions.keys()) {
      someFn(this.value);
    }
  }
}

TypeScriptPlayground链接编辑1

rt4zxlrg

rt4zxlrg1#

泛型类型ConditionalType<T>在给定联合时生成一个分布式类型。
例如,给定并集string | number时,将生成以下类型

type Foo = ConditionalType<string | number>
// ((v: string) => void) | ((v: number) => void)

请将条件的任意一侧括在方括号中以避免出现这种情况

type ConditionalType<T> = [T] extends [PrimitiveDataType]
  ? (v: T) => void
  : never;

上面的代码现在生成了下面的非分布式类型,它应该可以为抽象类工作

type Foo = ConditionalType<string | number>
// (v: string | number) => void

Playground

相关问题