简体   繁体   English

泛型`keyof``中的打字稿混淆扩展了keyof``typeof`等

[英]Typescript confusion in generic `keyof` `extends keyof` `typeof` and others

class Car {
    engine:number;
    detials:{
        good:'Boy'
    }
}

Class ModelProperty<T> when constructed with new ModelProperty<Car>('engine',22); 使用new ModelProperty<Car>('engine',22);构造类ModelProperty<T> new ModelProperty<Car>('engine',22); should work as engine is a property of Car and 22 as same type as engine ie number . 应该起作用,因为engineCar22的属性,与engine相同,即number

export class ModelProperty<T, P extends keyof T, V  = T[P]> {
    constructor(public name: P, public value: V) { }
    fun(t: T){
        let value: any = t[this.name]; // Should not be any
        let valueWhyError: V = t[this.name]; //Error.. Why?
    }
}
let engine2 = new ModelProperty<Car,'engine'>('engine','22'); // Gives error as '22' should be number.. working great.
let engine1 = new ModelProperty<Car,'engine'>('engine',2); // But there is repeatation 'engine', 'engine'
  1. In case of engine V should mean number . 如果engine V应该是指number But that line in function do gives error. 但是函数中的那一行do给出了错误。
  2. Constructor should not be <Car,'engine'> but only <Car> . 构造函数不应该是<Car,'engine'>而只能是<Car> Its following properties should be by default depending upon the argument. 默认情况下,它的以下属性取决于参数。
  3. How to make it work for nested property like new ModelProperty<Car>(['details','good'],'Girl') . 如何使它适用于嵌套属性,如new ModelProperty<Car>(['details','good'],'Girl')

For the first issue, the problem with your approach for the V parameter is that you specify the default for it, but that does not mean V MUST extend T[P] , just that that is the default, you can invoke the constructor with really any type parameter for V . 对于第一个问题,你对V参数的方法的问题是你指定它的默认值,但这并不意味着V必须扩展T[P] ,只是那是默认值,你可以真正调用构造函数V任何类型参数。 Just use T[P] where appropriate, as even if you constrain it properly ( V extends T[P] = T[P] ) the compiler it will still not be able to correctly follow that V is assignable from T[P] . 只要在适当的时候使用T[P] ,即使你正确地约束它( V extends T[P] = T[P] ),编译器仍然无法正确地遵循V可从T[P]分配。

export class ModelProperty<T, P extends keyof T> {
    constructor(public name: P, public value: T[P]) { }
    fun(t: T){
        let value = t[this.name]; // is T[P]
    }
}

As for your second issue of repetition, this is an unfortunate side effect of the way type parameters and inference work, if you specify a default for the generic parameter, that default will be used and no inference will take place. 至于你的第二个重复问题,这是类型参数和推理工作方式的一个不幸的副作用,如果你指定泛型参数的默认值,那么将使用默认值并且不会进行推理。 If you don't specify the default for K , you can't specify just the value for T , you must also specify K . 如果未指定K的默认值,则不能仅指定T的值,还必须指定K The simple workaround is to use a two function approach : 简单的解决方法是使用两种函数方法:

export class ModelProperty<T, P extends keyof T> {
    constructor(public name: P, public value: T[P]) { }

    static for<T>() {
        return function <P extends keyof T>(name: P, value: T[P]){
            new ModelProperty<T, P>(name, value);
        } 
    }
}
const carModelCreator = ModelProperty.for<Car>();
let engine2 = carModelCreator('engine','22'); // Gives error as '22' should be number.. working great.
let engine1 = carModelCreator('engine',2); // But there is repeatation 'engine', 'engine'

As for the third issue of nested paths, classes can't have a variable number of type parameters, so you have the option of creating dedicated classes for each path length. 至于第三期嵌套路径,类不能有可变数量的类型参数,因此您可以选择为每个路径长度创建专用类。

export class ModelProperty2<T, P extends keyof T,  P2 extends keyof T[P]> {
    constructor(public name: [P, P2], public value: T[P][P2]) { }

    static for<T>() {
        return function <P extends keyof T, P2 extends keyof T[P]>(name: [P, P2],  value: T[P][P2]){
            new ModelProperty2<T, P, P2>(name, value);
        } 
    }
}
const carModelCreator = ModelProperty2.for<Car>();
let engine2 = carModelCreator(['detials', 'good'],'22'); //error 
let engine2 = carModelCreator(['detials', 'good'],'Boy'); //ok

Or if you want you can create a single overloaded function that returns an instance ModelProperty where the only type parameter is the value of the last property, and the path is string[]. 或者,如果需要,可以创建一个返回实例ModelProperty重载函数,其中唯一的类型参数是最后一个属性的值,路径是string []。 You get type safety when you create the instance but the info is lost after 创建实例时会获得类型安全性,但之后信息会丢失

export class ModelProperty<T, V> {
    constructor(public name: string[], public value: V) { }


    static for<T>() {
        function helper<P extends keyof T, P2 extends keyof T[P]>(name: [P, P2],  value: T[P][P2])
        function helper<P extends keyof T>(name: [P],  value: T[P])
        function helper(name: string[],  value: any){
            return new ModelProperty<T, any>(name, value);
        } 

        return helper;
    }
}
const carModelCreator = ModelProperty.for<Car>();
let engine1 = carModelCreator(['engine'], 22); // ok
let engine2 = carModelCreator(['detials', 'good'],'Boy'); //ok

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM