【问题标题】:TypeScript provide (unspecified) generic type as generic argumentTypeScript 提供(未指定)泛型类型作为泛型参数
【发布时间】:2020-11-11 09:17:12
【问题描述】:

我希望提供一个泛型类型作为类型参数而不首先将其解析为具体类型。换句话说,我正在寻找一种方法来指定从基类继承时可以使用的类型映射函数。

示例(不正确)语法,希望比我能解释得更好:

abstract class RunIt<SomeMagicConverter> {
    // The return type for this function depends on the type of the
    // argument *and* on the way the implementation class was declared:
    run<T>(o: T): SomeMagicConverter<T> { // (syntax error)
        return this.handle(o); // (imagine this does something more interesting)
    }
    protected abstract handle<T>(o: T): SomeMagicConverter<T>; // (syntax error)
}

type MyMagicConverter<T> = TypeIWantToReturn<T>; // MyMagicConverter is generic
class MyRunIt extends RunIt<MyMagicConverter> { // but we don't specify T here
    // [...]
}
new MyRunIt().run(7); // call infers T is number, so returns TypeIWantToReturn<number>
new MyRunIt().run(''); // now T is string, so returns TypeIWantToReturn<string>

此外,我想限制这一点,以便保证SomeMagicConverter&lt;T&gt; extends SomeBase&lt;T&gt;。即

abstract class RunIt<SomeMagicConverter extends SomeBase>

对于我希望如何使用它的更具体的示例,这里有一个用于 wrap-with-caching 的基本基类(不是我的实际用例,但演示了需要):

interface Wrapped<T> {
    contains(other: T): boolean;
}

abstract class Store {
    private readonly cached = new Map<any, any>();

    protected abstract applyWrap<T>(o: T): Wrapped<T>;

    wrap<T>(o: T): Wrapped<T> { // <-- this should return something more specific
        if (!this.cached.has(o)) {
            this.cached.set(o, this.applyWrap(o));
        }
        return this.cached.get(o);
    }
}

class Foo<T> implements Wrapped<T> {
    constructor(private readonly o: T) {}
    contains(other: T): boolean { return other === this.o; }
    extraFooFunc(): void {}
}

class FooWrapper extends Store {
    constructor() { super(); }
    protected applyWrap<T>(o: T): Foo<T> { return new Foo<T>(o); }
}

new FooWrapper().wrap(4).extraFooFunc(); // syntax error because extraFooFunc is not defined on Wrapped

显然我可以通过定义包装方法来解决这个问题,但我想避免在每个子类上都这样做:

class FooWrapper extends Store {
    // [...]
    wrap<T>(o: T): Foo<T> { return super.wrap(o) as Foo<T>; }
}

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    尚不支持泛型泛型参数(2021 年 3 月)。请参阅TypeScript Issuesimilar SO question 了解更多信息。

    但是,您描述的可以使用Indexed Access TypesMapped Types 来实现:

    // Any Converter to be used with RunIt must be added to Converters
    interface Converters<T> { }
    
    // All Converters must implement this interface
    interface ConverterBase<T> { length: number }
    
    // Maps valid keys to themselves, invalid ones to `never`
    type ValidKey<T, K extends keyof Converters<T>> =
        Converters<T>[K] extends ConverterBase<T> ? K : never;
    
    // Contains all entries from Converters where the type extends ConverterBase
    type ConstrainedConverters<T> = {
        [K in keyof Converters<T> as ValidKey<T, K>]: Converters<T>[K];
    };
    
    abstract class RunIt<K extends keyof ConstrainedConverters<void>> {
        run<T>(o: T): ConstrainedConverters<T>[K] {
            return this.handle(o);
        }
        protected abstract handle<T>(o: T): ConstrainedConverters<T>[K];
    }
    
    // Re-open Converters and add some Converters
    interface Converters<T> { Id: T, Array: Array<T> }
    
    class ArrayRunIt extends RunIt<'Array'> {
        protected handle<T>(o: T) { return [o]; }
    }
    
    // @ts-expect-error Type '"Id"' does not satisfy the constraint '"Array"'.(2344)
    class IdRunIt extends RunIt<'Id'> {
        protected handle<T>(o: T) { return o; }
    }
    

    【讨论】:

    • 这个问题正是我要找的。我将不得不看看您提出的解决方法是否适合我,但这肯定是一种有趣的方法。我不知道可以扩展这样的接口。遗憾的是,泛型参数并没有真正限制为ConstrainedConverters,因此可能会意外地使具有never 的对象在某些方法上返回(实际错误与错误相差甚远),但它确实实现了约束它的基本思想。
    • @Dave 我解决了延迟错误的问题。我现在没有将无效的转换器映射到never,而是将相应的键映射到never,这会从ConstrainedConverters 中删除整个条目。这反过来会在向RunIt 提供无效密钥时立即导致错误。更新后的代码也包含一个示例。
    猜你喜欢
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-18
    • 2021-12-06
    • 2020-07-25
    • 2019-05-30
    • 1970-01-01
    相关资源
    最近更新 更多