typescriptdynamic-properties

How to use a type with static and dynamic typed properties


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.

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?


Solution

  • 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