Typescript-gränssnitt för dynamiskt objekt
Anta att jag kan ha objekt som detta:
const filters = {
name: { value: 'some-value', rank: 'some-value' },
place: { value: 'some-value', population: 'some-value' },
}
och det skulle också kunna vara så här...
const filters = {
name: { value: 'some-value', rank: 'some-value' },
place: { value: 'some-value', population: 'some-value' },
description: { value: 'some-value', translation: 'some-value' },
popularity: { value: 'some-value', ranking: 'some-value' },
}
Hur skapar jag ett gränssnitt för det (ett dynamiskt objekt)?
Svar #1
En möjlighet skulle vara att göra description och popularity valfria genom att lägga till en ?, så här:
Tillvägagångssätt 1
interface Filters {
name: object,
place: object,
description?: object,
popularity?: object,
}
För mer information om egenskapsmodifierare, se dokumentationen
Ett annat tillvägagångssätt skulle vara att använda gränssnittsarv genom att använda nyckelordet extends
Tillvägagångssätt 2
interface ParentFilters {
name: object,
place: object
}
Och för det härledda gränssnittet
interface derivedFilters extends parentFilter {
description: object,
popularity: object,
}
Svar #2
Det är inte möjligt att skapa en typ som tvingar till att vara { a } eller { a, b, c }. Detta kallas Discriminerade unioner och du kan läsa mer om detta problem här.
Du kan använda båda tillvägagångssätten som @SimonRosengren nämnde. För att tvinga typ-säkerhet när du definierar ett objekt måste du implicit säga vilken typ som är vad:
interface FilterTypeBase {
name: { value: string, rank: string },
place: { value: string, population: string }
}
interface FilterTypeExtended extends FilterTypeBase {
description: { value: string, translation: string },
popularity: { value: string, ranking: string }
}
// 👍 OK
const filters: FilterTypeBase = {
name: { value: 'some-value', rank: 'some-value' },
place: { value: 'some-value', population: 'some-value' },
}
// ❌ ERROR: Property 'popularity' is missing
const filters2: FilterTypeExtended = {
name: { value: 'some-value', rank: 'some-value' },
place: { value: 'some-value', population: 'some-value' },
description: { value: "string", translation: "string" }
}
Om du vill bestämma i en funktion vilken av båda varianterna din typ är. Du kan göra detta med insnävning:
type FilterType = FilterTypeBase | FilterTypeExtended
function doSomething(filter: FilterType) {
if('description' in filter) {
// Here it's the extended type
}
}
Typescript-dokumentation: Avsmalning
