Можно ли ограничить тип T включением подтипов K, но не K? - PullRequest
0 голосов
/ 17 января 2020

Можно ли ограничить универсальный c тип T набором подтипов типа K, не включая K? Я пытаюсь определить тип для функций смешивания на основе наследования. Вопрос 32488309 дает ответ для противоположного, и по совпадению этот вопрос задается, но остается без ответа в комментариях.

TypeScript Playground

// Define a type for the constructor signature.
interface IConstructor { new(...args: any[]): any; }

type Mixin = <T extends IConstructor, U extends T>(Base: T) => U;

// Mix a set of mixins.
function mix(...mixins: Mixin[]) {
  return mixins.reduce((child: IConstructor, mixer) => mixer(child), Object);
}

// Mix typically accepts Mixins with the same constructor signature.
interface ILocalized extends IConstructor {
  new(i18n: I18n): any;
}

function mixinOne(Base: ILocalized) {
  return class MixinOne extends Base {
    constructor(i18n: I18n) { super(i18n); }
  }
}

Это приводит к следующей ошибке из вопрос 56505560 , которая объясняет, что я достиг противоположности моей цели. T не может быть набором подтипов K, потому что, когда он является MixinOne, он не может быть любым другим.

const LocalizedBase: IBase = mix(mixinOne, ...);

'typeof MixinOne' можно назначить ограничению типа 'T', но 'T' может быть создан с другим подтипом ограничения 'IConstructor'.

В качестве альтернативы следующее устраняет ошибку, но включает K, несмотря на использование extends.

export type Mixin = (Base: IConstructor) => IConstructor;

Меня не интересует свойство, повторяющее альтернативное решение для миксинов, потому что эти классы требуют внедрения зависимостей.

1 Ответ

1 голос
/ 17 января 2020

Вот мое решение:

type RequireSubtype<A, B extends A> = A extends B ? never : B

Чтобы использовать его, напишите обобщенную c функцию, принимающую параметр типа B extends A и фактический параметр типа RequireSubtype<A, B>. Например:

class Foo {
    constructor(public readonly x: number) {}
}

class Bar extends Foo {
    constructor(x: number, public readonly y: number) { super(x); }
}

function requireSubtype<T extends Foo>(arg: RequireSubtype<Foo, T>): void {
    // ...
}

// ok
requireSubtype(new Bar(2, 3));
// type error: Argument of type 'Foo' is not assignable to parameter of type 'never'.
requireSubtype(new Foo(1));

Playground Link

...