Typescript: error TS2677: Type 'keyof E' is not assignable to type 'string'

Created on 27 Apr 2018  Β·  4Comments  Β·  Source: microsoft/TypeScript



TypeScript Version: Version 2.9.0-dev.20180426


Search Terms: keyof not assignable string

Code

//Just a minimal example, not what I am actually doing
function isKey<E> (str : string) : str is keyof E {
    return true;
}

Expected behavior:

Should compile fine. The body of the type guard may actually correctly assert that str is, indeed, keyof E

Works fine on TypeScript 2.8.3

Actual behavior:

error TS2677: A type predicate's type must be assignable to its parameter's type.
  Type 'keyof E' is not assignable to type 'string'.
    Type 'string | number | symbol' is not assignable to type 'string'.
      Type 'number' is not assignable to type 'string'

Playground Link: Works on the playground

Related Issues:

Breaking Change

Most helpful comment

This is a breaking change in TS 2.9 (documentation should come soon) introduced by https://github.com/Microsoft/TypeScript/pull/23592. With https://github.com/Microsoft/TypeScript/pull/23592 keyof now returns all known keys of a types including string, number and symbol keys. and as such, the constraint on keyof E is now string | number | symbol.

If you are interested in getting only the string portions of the keys, use Extract:

function isKey<E>(str: string): str is Extract<keyof E, string> {
    return true;
}

if your function is meant to handle all property names, including symbols, then consider widening the declaration of str:

function isKey<E> (str : string | number | symbol) : str is keyof E {
    return true;
}

All 4 comments

This is a breaking change in TS 2.9 (documentation should come soon) introduced by https://github.com/Microsoft/TypeScript/pull/23592. With https://github.com/Microsoft/TypeScript/pull/23592 keyof now returns all known keys of a types including string, number and symbol keys. and as such, the constraint on keyof E is now string | number | symbol.

If you are interested in getting only the string portions of the keys, use Extract:

function isKey<E>(str: string): str is Extract<keyof E, string> {
    return true;
}

if your function is meant to handle all property names, including symbols, then consider widening the declaration of str:

function isKey<E> (str : string | number | symbol) : str is keyof E {
    return true;
}

Also, as a temporary measure you can use the new --keyofStringsOnly compiler option to revert to the old behavior.

Thanks for the swift response!

However, if I specifically mention that my type has only string keys like this:

interface A { [k: string]: any; }
type b = keyof A;

still, I get the type of b as string | number as seen in the IntelliSense on vs code:
image
Why is that so? Why is it not string only?
Typescript used: 3.9.3

Was this page helpful?
0 / 5 - 0 ratings