Typescript-förvirring i generiska `keyof`, `extends keyof`, `typeof` och andra
class Car {
engine:number;
detials:{
good:'Boy'
}
}
Klass ModelProperty<T> när den konstrueras med new ModelProperty<Car>('engine',22); bör fungera eftersom engine är en egenskap hos Car och 22 är samma typ som engine dvs 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'
- I fallet med
enginebörVbetydanumber. Men den raden i funktionendoger fel. - Konstruktören bör inte vara
<Car,'engine'>utan endast<Car>. Dess följande egenskaper bör som standard bero på argumentet. - Hur man får det att fungera för en nästlad egenskap som
new ModelProperty<Car>(['details','good'],'Girl').
Svar #1
För den första frågan är problemet med ditt tillvägagångssätt för V-parametern att du anger standardvärdet för den, men det betyder inte att V MÅSTE utvidga T[P], bara att det är standarden; du kan anropa konstruktorn med vilken typ som helst för V. Använd bara T[P] där det är lämpligt, eftersom även om du begränsar det korrekt (V extends T[P]=T[P]) kommer kompilatorn ändå inte att kunna korrekt avgöra att V kan tilldelas från 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]
}
}
När det gäller ditt andra problem med upprepning, är detta en olycklig bieffekt av hur typparametrar och inferens fungerar. Om du anger ett standardvärde för den generiska parametern kommer det standardvärdet att användas och ingen inferens kommer att ske. Om du inte anger standardvärde för K, kan du inte bara ange värdet för T, du måste också ange K. Den enkla lösningen är att använda en tvåfunktionersmetod:
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'
När det gäller den tredje frågan om nästlade vägar, kan klasser inte ha ett variabelt antal typparametrar, så du har möjligheten att skapa dedikerade klasser för varje vägslängd.
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
Eller om du vill kan du skapa en enda överbelastad funktion som returnerar en instans av ModelProperty där den enda typparametern är värdet av den sista egenskapen, och vägen är string[]. Du får typkontroll när du skapar instansen men informationen går förlorad efteråt
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
