Почему это определение рекурсивного типа требует определения примитивов? - PullRequest
1 голос
/ 27 марта 2020

Почему это не компилируется, если я удаляю закомментированные строки для этого типа?

type DeepPartial<T> =
    T extends Array<any> ? T
    : T extends ReadonlyArray<any> ? T
    : T extends Function ? T
    : T extends number ? T // why is this needed
    : T extends string ? T // same thing...
    : T extends boolean ? T // same thing...
    : T extends bigint ? T // same thing...
    : T extends symbol ? T // same thing...
    : T extends object ? { [P in keyof T]?: DeepPartial<T[P]> }
    : T;

function test<T>(o: DeepPartial<T>) {
    if (o === 1) return 0;
    if (o === '') return 1;
    if (o === false) return 2;
    if (o === 1n) return 3;
    if (o === Symbol.hasInstance) return 4;
    if (typeof o === 'number' && o === 2) return 5; //or this needed?
    return 6;
}

Я думаю, что закомментированные строки в типе DeepPartial не нужны из-за конечного условия, но компилятор не согласен ( Playground Link ).

1 Ответ

0 голосов
/ 27 марта 2020

Указанное c сообщение об ошибке:

Это условие всегда будет возвращать «ложь», поскольку типы «DeepPartial » и «[число | строка | логическое значение | bigint | символ]» 'не перекрываются.

Это очень похоже на сообщение об ошибке, которое вы получите для всех, кроме последнего оператора if при определении функции как function test<T>(o: T):

Это условие всегда будет возвращать «ложь», поскольку типы «T» и «[число | строка | логический | bigint | символ]» не перекрываются.

Поскольку тип не был вначале распознается, что тип T является непрозрачным типом в теле test(), поэтому его следует рассматривать как unknown, а не any.

Для T нетипично ссылаться на неоднородная смесь типов, так что если это то, что вам нужно, то, вероятно, я бы предложил такой подход:

const enum Test {
  ONE,
  STRING,
  FALSE,
  BIG_ONE,
  HAS_INSTANCE,
  TWO,
  UNKNOWN
}

type TestMap<T> = T extends 1 ? Test.ONE
  : T extends '' ? Test.STRING
  : T extends false ? Test.FALSE
  : T extends 1n ? Test.BIG_ONE
  : T extends SymbolConstructor['hasInstance'] ? Test.HAS_INSTANCE
  : T extends 2 ? Test.TWO
  : Test.UNKNOWN;

function test<T>(o: T): TestMap<T> {
  function testImpl(o: any): Test {
    if (o === 1) return Test.ONE;
    if (o === '') return Test.STRING;
    if (o === false) return Test.FALSE;
    if (o === 1n) return Test.BIG_ONE;
    if (o === Symbol.hasInstance) return Test.HAS_INSTANCE;
    if (o === 2) return Test.TWO;
    return Test.UNKNOWN;
  }

  return testImpl(o) as TestMap<T>;
}
...