generics - Voitko määritellä objektin literal-tyypin, joka sallii tuntemattomat ominaisuudet TypeScriptissä?


Pohjimmiltaan haluan varmistaa, että olioargumentti sisältää kaikki vaaditut ominaisuudet, mutta voi sisältää myös muita ominaisuuksia, joita se haluaa. Esimerkiksi:

function foo(bar: { baz: number }) : number {
    return bar.baz;
}

foo({ baz: 1, other: 2 });

Mutta tästä seuraa:

Object literal may only specify known properties, and 'other' does not exist in type '{ baz: number; }'.

Vastaus #1

Kyllä, voit. Kokeile tätä:

interface IBaz {
    baz: number;
    [key: string]: any;
}

function foo(bar: IBaz) : number {
    return bar.baz;
}

foo({ baz: 1, other: 2 });

Vastaus #2

No, vihaan vastaamasta omiin kysymyksiini, mutta muut vastaukset inspiroivat hieman ajatusta... Tämä toimii:

function foo<T extends { baz: number }>(bar: T): void {
    console.log(bar.baz);
}

foo({baz: 1, other: 2});

Vastaus #3

Jos tunnetut kentät tulevat geneerisestä tyypistä, tapa sallia jokerimerkit on T & {[key: string]: unknown}, kaikki tunnetut kentät on sovitettava tyypin rajoituksiin ja muut kentät ovat sallittuja (ja niitä pidetään tyypin unknown arvoina)

Tässä on esimerkki:

type WithWildcards<T> = T & { [key: string]: unknown };

function test(foo: WithWildcards<{baz: number}>) {}

test({ baz: 1 }); // works
test({ baz: 1, other: 4 }); // works
test({ baz: '', other: 4 }); // fails since baz isn't a number

Sitten jos sinulla on geneerinen tyyppi T, voit sallia jokerimerkkikentät WithWildCards<T>-muodossa

Huomaa, että ylimääräisiä ominaisuuksia ei merkitä virheiksi, jos objekti tulee mistä tahansa muusta kuin objektiliteraalista; TS vain kertoo sinulle, että tyypityksen mukaan sen ominaisuuden lisääminen literaliin on tarpeetonta.

Tässä on joitakin muita tapoja, joissa lisäominaisuudet ovat sallittuja tai eivät ole sallittuja

interface Foos{
  a?: string
  b?: string
}
type WithWildcards<T> = T & { [key: string]: unknown };

declare function acceptFoos(foo: Foos): void;
declare function acceptWild(foo: WithWildcards<Foos>):void

acceptFoos(  {a:'', other: ''}) // ERROR since the property is extraneous
const data = {a:'', other: ''}
acceptFoos(  data) // allowed since it is compatible, typescript doesn't force you to remove the properties
const notCompat = {other: ''}
acceptFoos(notCompat) //ERROR: Type '{ other: string; }' has no properties in common with type 'Foos'
acceptFoos({a:'', ...notCompat}) // this is also allowed 
acceptWild(notCompat) // allowed

Vastaus #4

Jos haluat sallia tuntemattomat ominaisuudet koko objektissa, voit käyttää Record

function doStuff(payload: Record<string|number, unknown>): Record<string|number, unknown> {
  return { anyProp: 'anyValue' }
}

Aiheeseen liittyviä artikkeleita