typescript

How to add typescript index signatures with a suffix?


We work with an API that requires us to send off an object along with additional properties to tell it which properties on that object are to be modified by ading _X and setting it to true. For example if we had this object

{ firstName: 'Joe', lastName: 'Smith' }

and wanted to only change the lastName property, we would send this:

{firstName: 'Joe', lastName: 'Smith', lastName_X: true }

I'd like to set up strong typing for this in TypeScript, but I seem to be missing something

type ApiBoolean<T extends object> = {
    [index: `${K in keyof T}_X`]: true; // <== Error is here
};

function convertForApi<T extends object>(obj: T, keys: Array<keyof T>): ApiBoolean<T> {
    const returnObj: ApiBoolean<T> = {...obj}
    for(let k of keys){
        returnObj[`${k}_X`] = true;
    }
    return returnObj;
}

const person = {
    firstName: 'Joe',
    lastName: 'Smith',
    age: 35
};

const converted = convertForApi(person, ['lastName', 'age'])

Typescript Playground Link

I need to allow the indexer to have property suffixes. What am I doing wrong and how can I achieve this?


Solution

  • You could use an intersection of 2 mapped types:

    Playground

    type StringKeys<T extends object> = keyof {[K in keyof T as K extends string ? K : never]: unknown} & string;
    
    type ApiBoolean<T extends object, KK extends StringKeys<T>[]> =
    {[K in keyof T]: T[K]} & 
    {[K in KK[number] as `${K}_X`]: true} extends infer A ? A : never;
    
    function convertForApi<T extends object, const K extends StringKeys<T>[]>(obj: T, keys: K) {
        return keys.reduce((r, key) => (r[`${key}_X`] = true, r), {...obj} as any) as ApiBoolean<T, K>;
    }