How to add typescript index signatures with a suffix? - Stack Overflow

admin2025-04-15  0

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?

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?

Share Improve this question asked Feb 4 at 13:07 Chris BarrChris Barr 34.3k28 gold badges103 silver badges153 bronze badges 1
  • You can't make an index signature unless you're sure the key is appropriate for an index signature. Instead you really just want Record as shown in this playground link. Note how your initialization of returnObj is incorrect according to the typing, and so calling convertForApi(person, []) will return a completely inappropriate result. That's probably out of scope for the question, but it would help if you edit to remove distractions like this. Does this fully address the question? If so I'll write an answer or find a duplicate. If not, what's missing? – jcalz Commented Feb 4 at 14:49
Add a comment  | 

2 Answers 2

Reset to default 1

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>;
}

I think the issue is in the way you're using the literal types in your function. The way you're doing it is causing a TypeScript error because of how you're defining the keys in the mapped type.

I think this should fix your problem.

type ApiBoolean<T extends object> = {
    [K in keyof T as `${K & string}_X`]: true;
};
转载请注明原文地址:http://www.anycun.com/QandA/1744716878a86640.html