Reputation: 1421
Another inference question. I'm trying to prevent Typescript from losing properly inferred type information when I have a union type buried in an object. Here's the situation:
Two interfaces--Foo and Moo--and their union FooMoo:
interface Foo { goo: string}
interface Moo { goo: number}
type FooMoo = Foo | Moo
const foo: Foo = {goo: "bar"}
const moo: Moo = {goo: 1}
And a mixed object that contains (at least) one of each:
const mixedObject = {
foo,
moo
}
Now, obviously, TS knows the correct types of foo and moo, so no problem here:
let fooToo: Foo
let mooToo: Moo
fooToo = mixedObject.foo
mooToo = mixedObject.moo
But now let's say I want to tell TS that mixedObject should be limited to only foos and moos. Well, that restriction apparently causes TS to lose its ability to discriminate between foo and moo, so the following code errors:
type ManyFooMoos = {[K:string]: FooMoo}
const mixedObjectToo: ManyFooMoos = {
foo,
moo
}
// TS errors here because it can't narrow foo to Foo and moo to Moo;
// it just knows they are both FooMoos
fooToo = mixedObjectToo.foo
mooToo = mixedObjectToo.moo
This same thing can come up in other ways where the constraint on mixedObject that causes TS to lose information is more indirect. For example:
interface ExportedObject {
manyFooMoos: ManyFooMoos
}
const exportedObject: ExportedObject = {
manyFooMoos: mixedObject
}
fooToo = exportedObject.manyFooMoos.foo
So, my basic question is -- how can I keep the constraint on mixedObject (i.e., that all it's properties must be Foos or Moos) without losing the specific types of those Foos or Moos within mixedObject?
The full code is on this playground.
Upvotes: 1
Views: 262
Reputation: 37928
You can achieve this by using identity function + generics. This will allow to keep the constraint and prevent type widening:
const createFooMoos = <T extends ManyFooMoos>(fooMoos: T) => fooMoos;
const mixedObjectToo = createFooMoos({
foo,
moo
}) // { foo: Foo, moo: Moo }
Upvotes: 3