Reputation: 187194
I'm reading data from a file, which may have more data in it than my code cares about. I have a typescript type for each object type I want to process. But in the case of some unsupported object type, I want do something with that object.
The problem is that typescript thinks I have exhausted all possibilities and that my default clause is impossible to get to.
// Untyped example data source.
function loadFromSomeFile(): any {
return [{ objType: "A", a: 1 }, { objType: "B", b: 2 }] as any
}
// Union type of supported data from data source.
type A = { objType: "A", a: number }
type B = { objType: "B", b: number }
type ObjTypes = A | B
// Load the data.
const arr: ObjTypes[] = loadFromSomeFile()
// Switch on the type of each object.
for (const obj of arr) {
switch (obj.objType) {
case "A":
console.log('A', obj.a)
break
case "B":
console.log('B', obj.b)
break
default:
// Fall though case for unsupported objType
console.log('unkown objType: ' + obj.objType)
// ^ TS Error: objType does not exist on type 'never'
}
}
Error on Typescript Playground
I thought of trying to add a third option to the union like:
type X = { objType: string } // unknown
type ObjTypes = A | B | X
But now when obj.objType === 'A'
typescript can't tell if it's an A
or an X
since it's a valid type for both.
How can I tell typescript that there may also be unknown and unhandled values, and that my list is not exhaustive?
Upvotes: 3
Views: 324
Reputation: 187194
After wrestling with this for a while, I decided to give the unknown types a concrete type with a fixed value, that is incorrect.
type A = { objType: "A", a: number }
type B = { objType: "B", b: number }
type Unknown = { objType: '___unknown-obj-type___' }
type ObjTypes = A | B | Unknown
This causes the default clause of the switch fallthrough to this type. And I can still access the objects properties at runtime for real values.
It feels a bit like a hack but this has the least amount of code readability compromises so far, IMHO.
Upvotes: 1
Reputation: 12376
Since you can get not only A or B from the file, don't declare the type as A | B.
See the Playground
Upvotes: 0
Reputation: 1700
Using:
return [
{ objType: "A", a: 1 },
{ objType: "B", b: 2 },
{ objType: "C" }
{ foo: "bar" }
] as any
as the data, and setting the type to be:
type ObjTypes = A | B | any
Then, inside each of your case
blocks, cast the object to the type expected, either A
or B
, etc.
case "A":
let a = <A>obj;
console.log('A', a.a)
//console.log('A', a.x) // TS error!
break
logs to the console:
A 1
B 2
unknown objType: C
unknown objType: undefined
See updated Typescript Playground
Upvotes: 0