Create interface that has optional unknown properties of an unknown type

682 views Asked by At
interface Example {
    value?: string
    [prop: string]: any
}

const xxx: Example = { name: 'Thomas' }

const v = xxx.name

Adding "any" here actually removes the inferred "string" type on name that would otherwise be there if xxx was not assigned to Example.

Is there any way to have a interface or type that passed the inferred type?

3

There are 3 answers

0
Karol Majewski On
interface DictionaryLike {
    [index: string]: unknown
}

interface Example extends DictionaryLike {
  name: string;
}

const xxx: Example = { name: 'Thomas' }

const v = xxx.name; // string
const w = xxx.foo; // unknown
0
christian On

You can use a generic type with a union:

type WithName<T> = T & { name?: string };
const myThing: WithName<{ foo?: string; bar?: number }> = { name: 'sup'};
0
kaya3 On

This can be done using a generic Record type:

type Example<K extends PropertyKey, V> = Record<K, V> & {
    // non-generic interface properties go here
    value?: string
};

const xxx: Example<'name', string> = { name: 'Thomas' };

const b = xxx.name; // ok, inferred as string
const x = xxx.address; // not ok

To avoid having to write the explicit type parameters in Example<'name', string>, you can use a generic identity function:

function example<K extends PropertyKey, V>(e: Example<K, V>): Example<K, V> {
    return e;
}

const xxx = example({ name: 'Thomas' });

The constant xxx will be inferred to have the type Example<'name', string> as required.