I am trying to use a type that has all the properties of one type, but also allows adding dynamic properties of another type (similar to this question).
Types are defined like this:
type Foo = string | string[]
type Bar = { n: number, str?: string }
// A dictionary which only allows Foo values
type FooDict = { [key: string]: Foo | undefined }
// Has all static properties of Bar plus some dynamic Foo properties
type FooBar = FooDict & Bar
and I am expecting to use them like this:
const bar: Bar = { n: 1 }
const fooBar: FooBar = bar
const n: number = fooBar.n
const str: string | undefined = fooBar.str
const foo: Foo | undefined = fooBar['someRandomKey']
Problem is that the second line gives a compile error: "Type 'Bar' is not assignable to type 'FooBar'. Type 'Bar' is not assignable to type 'FooDict'. Property 'n' is incompatible with index signature. Type 'number' is not assignable to type 'Foo | undefined'. TS2322".
I don't see any reason why it couldn't work.
Bar
and FooDict
are strings, so there is no mismatch.Bar
, we can use the corresponding static property of Bar.FooDict
, which is either Foo
(when the property exists) or undefined
(when using a key with no value).As mentioned in the linked question, this works if the dynamic properties are typed as any
. But since I only need to set Foo instances into the dynamic properties, I would like to enforce it with the type system. Is this possible?
The short answer is that FooBar
and Bar
are not compatible since FooBar
promises a string based index signature while Bar
does not. (Basically that is what the error message says)
const fooBar: FooBar = {...}
const bar: Bar = fooBar;
// this will work since foobar has a string index signature
fooBar['someRandomKey'] // undefined | Foo
// this will give a compilation error since there is no string based index signature
bar['someRandomKey'] // NOTE: it will work at runtime though (which is why it is so confusing)
If you want to assing Bar
to Foobar
you have to provide a respective signature which will basically equal your FooBar
type.
Assigning FooBar
to Bar
should be always possible