arv - Typescript konstruktørparametre baseret på klasse- og underklasseegenskaber
Lad os antage, at jeg ønsker at genbruge en grundlæggende konstruktør i underklasser, men dens parameter vil afhænge af egenskaberne i underklassen.
Her er et grundlæggende (ikke-arbejdende) eksempel på, hvad jeg prøver at opnå.
class Foo {
a: string = 'a'
constructor(params?: { [key in keyof this]: this[key] }) { // this doesn't work as 'this' is not permited static members.
Object.assign(this, params)
}
}
class Bar extends Foo {
b: string = 'b'
// constructor inherited accepting { a: string, b: string }
}
const myFoo = new Foo({ a: 'foo' })
const myBar = new Bar({ a: 'foo', b: 'bar' })
Er der en eller anden måde at gøre det i TS på?
Legeplads
Svar #1
For Foo kunne du bruge params?: Foo, og så ville new Foo({a: "ayy"}) fungere, men Bar ville forvente en Foo, ikke en Bar.
Desværre tror jeg ikke, der er noget, du kan gøre for at ændre det, andet end at tilføje en constructor i Bar. Den constructor behøver selvfølgelig ikke at gøre meget, bare super(params).
class Foo {
a: string = "a";
constructor(params?: Foo) {
Object.assign(this, params);
}
}
class Bar extends Foo {
b: string = "b";
constructor(params?: Bar) {
super(params);
}
}
Legeplads link
Det ville være rart, hvis params?: typeof this (eller en anden afledning af det) ville fungere (og håndtere ændringen i betydning på tværs af underklasser), men det er ikke tilfældet i øjeblikket.
Svar #2
Måske ikke præcis, hvad du leder efter, men du kan bruge generics. Underklassetypen vil blive sendt til baseklassen som en generic, når du definerer en ny underklasse.
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
type DefaultFoo = Foo<any>;
class Foo<T extends Foo = DefaultFoo> { // Can't default to Foo here, it errors: Type parameter 'T' has a circular default.(2716)
a: string = 'a'
constructor(params?: Partial<NonFunctionProperties<T>>) {
Object.assign(this, params)
}
}
class Bar extends Foo<Bar> {
b: string = 'b'
}
new Foo({ a: 'foo' }) // no error
new Bar({ a: 'foo', }) // no error
new Bar({ a: 'foo', b: 'bar' }) // no error
new Foo({ b: 1 }) // error: Object literal may only specify known properties, and 'b' does not exist in type 'Partial<NonFunctionProperties<DefaultFoo>>'.
new Bar({ a: 1 }) // error: Type 'number' is not assignable to type 'string'.
new Bar({ ab: '' }) // error: Object literal may only specify known properties, and 'ab' does not exist in type 'Partial<NonFunctionProperties<Bar>>'.
TypeScript legeplads
For detaljer om NonFunctionProperties se dette
