首页 > 解决方案 > 在这些情况下“K 不能用于索引类型”的原因

问题描述

TypeScript 版本:4.1.5(严格模式)

我的问题

我在这里似乎缺少 TypeScript 类型系统的一个元素。

我不确定为什么在以下情况下会出现相应的 TypeScript 错误。我怀疑它们之间存在某种等价关系。

请注意,在 getOption 1、2 和 3 中,TypeScript 如何产生错误。但是,在 getOption 4 和 5 中,没有这样的错误。请任何人帮助我理解为什么在这种情况下,1-3 有错误,但 4-5 没有。

用例

我希望 A 的派生类包含扩展的 Options 属性,使用扩展的 Options 接口。而且我希望基类中的方法始终推断与调用上下文中的当前实例关联的类型。

示范

interface Options {
    test: boolean;
}

class A {
    private options: Options;
    
    constructor(options: Options) {
        this.options = options;
    }
    
    // Setting the 'this' arg doesn't change anything
    getOptions(this: A): Options {
        return this.options;
    }
    
    // Doesn't work - TS2536: Type 'K' cannot be used to index type 'Options'.
    getOption1<O extends Options, K extends keyof O>(name: K): O[K] {
        const options = this.getOptions();
        
        return options[name];
    }

    // Doesn't work - TS2536: Type 'keyof ReturnType' cannot be used to index type 'Options'.
    getOption2(name: keyof ReturnType<this['getOptions']>): ReturnType<this['getOptions']>[keyof ReturnType<this['getOptions']>] {
        const options = this.getOptions();
        
        return options[name];
    }

    // Doesn't work - two of the same error:
    // TS2536: Type 'keyof ReturnType' cannot be used to index type 'Options'.
    getOption3(name: keyof ReturnType<this['getOptions']>): Options[typeof name] {
        const options = this.getOptions();
        
        // TS2536: Type 'keyof ReturnType ' cannot be used to index type 'Options'.
        return options[name];
    }

    // Works
    getOption4(name: keyof Options): Options[typeof name] {
        const options = this.getOptions();

        return options[name];
    }

    // Works
    getOption5(name: keyof Options): ReturnType<this['getOptions']>[typeof name] {
        const options = this.getOptions();

        return options[name];
    }
}

谢谢你。

标签: typescripttypes

解决方案


问题是选项也可以是子类型,因此与其他选项类型不匹配。

这有效:

interface Options {
    test: boolean;
}

class A {
    private options: Options;
    
    constructor(options: Options) {
        this.options = options;
    }
    
    // Setting the 'this' arg doesn't change anything
    getOptions(this: A): Options {
        return this.options;
    }
    
    getOption1<O extends Options, K extends keyof O>(name: K): O[K] {
        const options = this.getOptions() as O;
        
        return options[name];
    }

    getOption2(name: keyof ReturnType<this['getOptions']>): ReturnType<this['getOptions']>[keyof ReturnType<this['getOptions']>] {
        const options = this.getOptions() as ReturnType<this['getOptions'];
        
        return options[name];
    }

    getOption3(name: keyof ReturnType<this['getOptions']>): ReturnType<this['getOptions']>[typeof name] {
        const options = this.getOptions() as ReturnType<this['getOptions']>;
        
        // TS2536: Type 'keyof ReturnType ' cannot be used to index type 'Options'.
        return options[name];
    }

    // Works
    getOption4(name: keyof Options): Options[typeof name] {
        const options = this.getOptions();

        return options[name];
    }

    // Works
    getOption5(name: keyof Options): ReturnType<this['getOptions']>[typeof name] {
        const options = this.getOptions();

        return options[name];
    }
}

但在这种特定情况下,您可能希望为选项创建一个通用类参数:

class A<TOptions extends Options> {
    private options: TOptions;
    
    constructor(options: TOptions) {
        this.options = options;
    }
    
    getOptions(): TOptions {
        return this.options;
    }
    
    getOption<K extends keyof TOptions>(name: K): TOptions[K] {
        const options = this.getOptions();
        
        return options[name];
    }
}

推荐阅读