Typescript - deklarera ett objekt som endast har egenskaper av en viss typ?


Jag försöker skapa ett objekt där alla värden är av typen SFXElem. Är det möjligt att säga till TS att varje värde måste vara av typen SFXElem? Precis som Array<string> vet att varje värde i arrayen kommer att vara en sträng?

interface SFXElem {
    path: string,
    audio?: Audio
}

// This doesn't work: Error "Type 'Object' is not generic
const sfxDir1: Object<SFXElem> = {
    sound1: {path: "something.mp3"},
    sound2: {path: "other.mp3"},
}

// This could work, but I can sneak other types if I don't use <SFXElem> on every line
const sfxDir2 = {
    sound1: <SFXElem>{path: "hi"},
    sound2: <SFXElem>{path: "ho"},
    sound3: {other: "hello"}      // Allowed
};

Finns det ett sätt att tala om för TypeScript att endast SFXElem-typer tillåts som objektvärden?

Försök 1:

Jag försökte använda Record<string, SFXElem>, som föreslagits av CRice, men det låter mig komma åt egenskaper som inte finns. I demonstrationen nedan är det tillåtet att komma åt egenskapen doesntExist, när det borde ge ett fel.

Svar #1

Du kan använda indexsignaturer.

interface SFXObject: {
    [index: string]: SFXElement
}

På det här sättet kan du skapa ett objekt av typen SFXObject och varje strängegenskap måste vara ett SFXElement.

Så i ditt fall något sådant här:

const sfxDir1: SFXObject = {
    sound1: {path: "something.mp3"},
    sound2: {path: "other.mp3"},
    sound3: {other: "hello"} // Not allowed
}

Dokumentation här: https://www.typescriptlang.org/docs/handbook/2/objects.html

Svar #2

Du kan använda Record genom att lista namn på egenskaper som du vill tillåta:

const sfxDir3: Record<"sound1" | "sound2", SFXElem> = {
    sound1: {path: "something.mp3"},
    sound2: {path: "other.mp3"},
};

Det verkar som att du skapar ett hönan-eller-ägget-problem här. Om du vill tillåta "vilket som helst" egenskapsnamn på typen och bara begränsa egenskapstypen, då är ditt andra exempel Record<string, SFXelem> giltigt.

Detta skulle vara giltig användning för det fallet, att lägga till en extra egenskap efter initialisering:

const sfxList2: Record<string, SFXElem> = {
    sound1: {path: "something.mp3"},
    sound2: {path: "other.mp3"},
};

sfxList2.doesntExistsYet = {path: "yet-another.mp3"};

Relaterade artiklar