Marko
Marko

Reputation: 1337

Why don't template literal types work for names of object attributes

I have this simple JavaScript function

function retObj(suff, n) {
  const r = {[`fixed${suff}`]: n * 2};
  return r;
}

retObj('A', 3) // { fixedA: 6 }

Now if I want to add types to it, I tried like this:

type FixedWithSuffix<T extends string> = `fixed${T}`;

type ObjWithFixedField<T extends string> = {[k in FixedWithSuffix<T>]: number};

function retObj<T extends string>(suff: T, n: number): ObjWithFixedField<T> {
  return {[`fixed${suff}`]: n * 2};
}

Compiler reports error (although without strict mode it compiles and works):

Type '{ [x: string]: number; }' is not assignable to type '{ [k in `fixed${T}`]: number; }'.(2322)

But the same thing works in a bit simpler situation, where function returns string:

type FixedWithSuffix<T extends string> = `fixed${T}`;

function retStr<T extends string>(suff: T): `fixed${T}` {
  return `fixed${suff}`;
}

Here is the code on TypeScript Playground.

Why is that? Is it possible to add proper types to that function?

Upvotes: 1

Views: 34

Answers (1)

tenshi
tenshi

Reputation: 26332

The cleanest way is to probably move it into an overload:

type FixedWithSuffix<T extends string> = `fixed${T}`;

type ObjWithFixedField<T extends string> = { [k in FixedWithSuffix<T>]: number };

function retObj<T extends string>(suff: T, n: number): ObjWithFixedField<T>;
function retObj(suff: string, n: number) {
    const r = { [`fixed${suff}`]: n * 2 };

    return r;
}

Then you won't have to deal with the annoying types in the body.

Playground

Upvotes: 2

Related Questions