Skip to content

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

Description

@AnyhowStep

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:

Activity

  1. mhegazy commented on Apr 26, 2018

    @mhegazy
    Contributor

    This is a breaking change in TS 2.9 (documentation should come soon) introduced by #23592. With #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;
    }
  2. ahejlsberg commented on Apr 26, 2018

    @ahejlsberg
    Member

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

  3. AnyhowStep commented on Apr 27, 2018

    @AnyhowStep
    ContributorAuthor

    Thanks for the swift response!

  4. added a commit that references this issue on Jun 25, 2018
  5. abdatta commented on Jun 4, 2020

    @abdatta

    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

  6. SLGShark6 commented on Jan 30, 2021

    @SLGShark6

    For typescript 4, I use this in place of keyof as a current workaround to get accurate key types

    type KeyTypes<T> = {
        [K in keyof T]-?: K extends string ?
            string :
            K extends number ?
                number :
                K extends symbol ?
                    symbol :
                    never
    }[keyof T];
    
    type KeyOfType<T, KeyType extends string | number | symbol = KeyTypes<T>> = Extract<keyof T, KeyType>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Breaking ChangeWould introduce errors in existing code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions