bunch of different ways to do this, i'd recommend either a tuple type or a const assertion
Check out this TS playground link for an interactive example with the following code:
type House = {
street: string;
zipCode: number;
}
type Car = {
make: string;
model: string;
year: number
}
const thingsA: [House, Car] = [{
street: '123 Fake Street',
zipCode: 12345
}, {
make: 'Tesla',
model: 'Model X',
year: 2022
}]
let aHouse = thingsA[0]
let aCar = thingsA[1]
// @ts-expect-error
let aError = thingsA[2]
const thingsB = [{
street: '123 Fake Street',
zipCode: 12345
}, {
make: 'Tesla',
model: 'Model X',
year: 2022
}] as const
let bHouse: House = thingsB[0]
let bCar: Car = thingsB[1]
// @ts-expect-error
let bError: Car = thingsB[0]
with real data, i find tagged unions aka discriminated unions (aka ADTs?) to be the best way to deal with this
playground link
type House = {
_tag: "house";
street: string;
zipCode: number;
}
type Car = {
_tag: "car",
make: string;
model: string;
year: number
}
const thingsA: Array<House|Car> = [{
_tag: "house",
street: '123 Fake Street',
zipCode: 12345
}, {
_tag: "car",
make: 'Tesla',
model: 'Model X',
year: 2022
}]
for (const thing of thingsA) {
switch (thing._tag){
case "house": {
// now we know it's a house...
console.log(thing.street)
break
}
case "car": {
// now we know it's a car...
console.log(thing.model)
break
}
}
}
things[x].year) leads to some possibilities. If you type check the value first, then TypeScript can infer it correctly. :-)