1

I have a simple function that uses the keyof feature:

interface B {
  name: string;
  age: number;
}

function test(key: keyof B) {}

const c = {
  age: 'age'
}

test(c.age);

The issue with the above code is that typescript throws an error that type string is not assignable to keyof B.

So what is the point of the keyof feature if it's not working with object key value? I don't want to add as keyof B.

3 Answers 3

2

The problem is that typescript does not infer by default literal types for object properties (even if they are const).

You can get around this by using a type assertion to the string literal type:

const c = {
  age: 'age' as 'age'
}

test(c.age);

Or of you want to infer string literal types for all properties of an object you can use a function (I use a iffe here but it can be a separate function)

const c = (<V extends string,  T extends Record<string, V>>(o: T) => o)({
  age: 'age'
});

test(c.age);

Or in 3.4 (not yet released) you can use a as const assertion on the object:

const c = {
  age: 'age'
} as const

test(c.age);
Sign up to request clarification or add additional context in comments.

11 Comments

I don't think that would work, either. The type assertion needs to be at the level of the object, not of the value you give it.
@MadaraUchiha works just fine: typescript-play.js.org/#code/…
@undefined The more common case is that you have an object and are allowed to change properties on it (rather than you have an object and must not modify it).
@undefined :)) welcome to typescript, some disappointment guaranteed :P
@undefined it's not a "feature", but rather an unfortunate side effect of one of the most useful features of the language. In almost all cases typescript's inference is excellent but sometimes you need to be more explicit than you'd like. I wouldn't let you turn this off TypeScript, you can do some awesome stuff with keyof especially when working with generics
|
2

c.age will be a string because of the implicit typing (c will be of type {age:string}). You can define the type for c.age to be keyof B. This will also restrict you in assigning other values to the age property and use them accidentaly.

interface B {
  name: string;
  age: number;
}

function test(key: keyof B) { }

const c: { age: keyof B } = {
  age: 'age'
}

test(c.age);

Comments

2

The reason is that TypeScript infers c to be of type {age: string;}, and not {age: 'age';}.

Had it inferred the latter, instead of the former, you wouldn't have been able to change c.age like so:

const c = { age: 'age' }
c.age = 'somethingElse';

Had you called it with the literal 'age' directly, it would have worked as advertised.

You can override TypeScript inference with a proper type like so:

const c: {age: 'age'} = {
  age: 'age',
};

And that would both make the type error disappear, and enforce that you do not assign anything to c.age other than the literal string 'age'.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.