网站搜索

typescript - 如何获取泛型类的内部类型


假设我有以下代码:

interface Outer<T = any> {
    name: string;
    body: T;
}

interface MyClass<T extends Outer> {
    myFunction2: typeof myFunction2;
};

class MyClass<T extends Outer> {
    data: T;

    myFunction1() {
        return this.data.body;
    }

}

MyClass.prototype.myFunction2 = myFunction2;

function myFunction2<T extends MyClass<Outer<R>>, R>(this: T): R {
    return this.data.body;
}

如果我现在创建该类的一个实例并尝试直接访问 body 属性,它将按预期工作:

let myClass = new MyClass<Outer<number>>();
const direct = myClass.data.body; // number

但是如果我尝试通过一个函数访问该属性,我无法让它工作。

const f1 = myClass.myFunction1(); // is 'any' but expecting 'number'  
const f2 = myClass.myFunction2(); // is '{}' but expecting 'number'

我如何才能让 TypeScript 了解内部类型(在此示例中 body 的类型)?我知道可以向 TypeScript 提供更多信息,例如:

const f2 = myClass.myFunction2<MyClass<Outer<number>>, number>(); // number

但那非常冗长,而且我会失去一些类型安全性。

问题:我能否以某种方式重写 MyClass,使 TypeScript 能够识别 myFunction1 和 myFunction2 的返回值?我主要关注如何解决 myFunction2(扩展原型)的情况,不过 myFunction1 也很有趣。

TypeScript playground 中的代码。

谢谢!

答案#1

我刚刚发现了 T['key'] 语法,它可以用来获取一个内部类型:

interface Outer<T = any> {
    name: string;
    body: T;
}

interface MyClass<T extends Outer> {
    myFunction2: typeof myFunction2;
};

class MyClass<T extends Outer> {
    data: T;

    myFunction1(): T['body'] {
        return this.data.body;
    }

}

MyClass.prototype.myFunction2 = myFunction2;

function myFunction2<T extends Outer>(this: MyClass<T>): T['body'] {
    return this.data.body;
}

所以现在我可以得到“内部”类型 🎉

let myClass = new MyClass<Outer<number>>();
const direct = myClass.data.body; // number
const f1 = myClass.myFunction1(); // number
const f2 = myClass.myFunction2(); // number

编辑:链接

答案#2

问题是,遗憾的是,TypeScript 无法在泛型类型中推断更深层的属性。它还会急切地解析类型变量,因此 myFunction1 的类型就好像 T 是 Outer。因此它的返回值是 any。你需要在 data 中显式地指定 body 属性的类型。

interface Outer<T = any> {
    name: string;
    body: T;
}

class MyClass<T> {
    data: Outer<T>;
    myFunction2: typeof myFunction2;

    myFunction1() {
        return this.data.body;
    }

}

MyClass.prototype.myFunction2 = myFunction2;

function myFunction2<T extends MyClass<R>, R>(this: T): R {
    return this.data.body;
}

let myClass = new MyClass<number>();
const direct = myClass.data.body; // number
const f1 = myClass.myFunction1(); // now is number
const f2 = myClass.myFunction2(); // still {}, due to inference constraint
const f3 = myClass.myFunction2<MyClass<number>, number>(); // number

链接