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

Relaterede artikler