What does “keyof typeof” mean in TypeScript?

后端 未结 4 1155
渐次进展
渐次进展 2020-12-24 00:11

Example:

Explain to me what keyof typeof means in TypeScript

enum ColorsEnum {
    white = \'#ffffff\',
    black = \'#000000\',
}

type         


        
相关标签:
4条回答
  • 2020-12-24 00:47

    An enum creates an instantiated object. With typeof we get the auto generated type of this enum.

    Now we can get all indices with keyof to make sure Colors can only contain one of them.

    0 讨论(0)
  • 2020-12-24 00:52

    For finding the type of any values we use typeof operation. For eg

    const user = {
       getPersonalInfo(){},
       getLocation(){}
    }
    

    Here user is a value so here typeof operator comes in handy

    type userType = typeof user
    

    Here userType gives type information that user is an object which have two properties getPersonalInfo and getLocation and both are functions return void

    Now if you want to find the keys of user you can use keyof

    type userKeys = keyof userType
    

    which says userKeys= 'getPersonalInfo'| 'getLocation'

    Beware if you try to get user's key like type userKeys = keyof user you will get an error 'user' refers to a value, but is being used as a type here. Did you mean 'typeof user'?

    0 讨论(0)
  • 2020-12-24 00:53

    Common misconception about TypeScript

    TypeScript is often described as a type layer on top of JavaScript runtime. As if types and values lived on separate planes. However, in TypeScript, some things are types and values at the same time.

    This is true for:

    • classes,
    • enums,
    • namespaces.

    When can you use keyof?

    The keyof keyword only works on the type level. You cannot apply it to a JavaScript value.

    When do you need keyof typeof?

    When you're dealing with something that is a type and a value at the same time (like a class or an enum), but you're interested specifically in what the type of that value is.

    The simplest example:

    const foo = { bar: 42 }; // foo is a value
    type Foo = typeof foo; // Foo is the type of foo
    
    type KeyOfFoo = keyof Foo; // "keyof Foo" is the same as "keyof typeof foo", which is "bar"
    

    In general, when you see this:

    type A = keyof typeof B;
    

    the typeof B part tells TypeScript to look at the type of B. You can think of it as casting B to its type. Sort of like casting a two-dimensional object to a one-dimensional space.

    Since typeof B is a type, not a value, we can now use keyof on it.

    Example

    Classes are types and values. You can call them, but you can also use keyof on them.

    declare class Foo {
        static staticProperty: string;
    
        dynamicProperty: string;
    }
    
    type Constructor = typeof Foo;
    type Instance = Foo;
    
    type A = keyof Constructor; // "prototype" | "staticProperty"
    type B = keyof Instance; // "dynamicProperty"
    

    By using typeof together with keyof, we can toggle between using keyof against the instance type and the constructor type.

    0 讨论(0)
  • 2020-12-24 01:08

    To understand the keyof typeof usage in Typescript, first you need to understand what are literal types and union of literal types. So, I'll explain these concepts first and then explain keyof and typeof individually in detail. After that, I'll come back to enum to answer what is asked in the question. It's a long answer but examples are easy to understand.


    Literal types

    Literal types in Typescript are more specific types of string, number or boolean. For example, "Hello World" is a string, but a string is not "Hello World". "Hello World" is a more specific type of type string, so it is a literal type.

    A literal type can be declared as following:

    type Greeting = "Hello"
    

    This means that the object of type Greeting can have only a string value "Hello" and no other string value or any other value of any other type as shown in the following code:

    let greeting: Greeting
    greeting = "Hello" // OK
    greeting = "Hi"    // Error: Type '"Hi"' is not assignable to type '"Hello"'
    

    Literal types are not useful on their own, however when combined with union types, type aliases and type guards they become powerful.

    Following is an example of union of literal types:

    type Greeting = "Hello" | "Hi" | "Welcome"
    

    Now the object of type Greeting can have the value either "Hello", "Hi" or "Welcome".

    let greeting: Greeting
    greeting = "Hello"       // OK
    greeting = "Hi"          // OK
    greeting = "Welcome"     // OK
    greeting = "GoodEvening" // Error: Type '"GoodEvening"' is not assignable to type 'Greeting'
    

    keyof only

    keyof of some type T gives you a new type that is a union of literal types and these literal types are the names of the properties of T. The resulting type is a subtype of string.

    For example, consider the following interface:

    interface Person {
        name: string
        age: number
        location: string
    }
    

    Using the keyof operator on the type Person will give you a new type as shown in the following code:

    type SomeNewType = keyof Person
    

    This SomeNewType is a union of literal types ("name" | "age" | "location") that is made from the properties of type Person.

    Now you can create objects of type SomeNewType:

    let newTypeObject: SomeNewType
    newTypeObject = "name"           // OK
    newTypeObject = "age"            // OK
    newTypeObject = "location"       // OK
    newTypeObject = "anyOtherValue"  // Error...
    

    keyof typeof together on an object

    As you might already know, the typeof operator gives you the type of an object. In the above example of Person interface, we already knew the type, so we just had to use the keyof operator on type Person.

    But what to do when we don't know the type of an object or we just have a value and not a type of that value like following?

    const bmw = { name: "BMW", power: "1000hp" }
    

    This is where we use keyof typeof together.

    The typeof bmw gives you the type: { name: string, power: string }

    And then keyof operator gives you the literal type union as shown in the following code:

    type CarLiteralType = keyof typeof bmw
    
    let carPropertyLiteral: CarLiteralType
    carPropertyLiteral = "name"       // OK
    carPropertyLiteral = "power"      // OK
    carPropertyLiteral = "anyOther"   // Error...
    

    keyof typeof on an enum

    In Typescript, enums are used as types at compile-time to achieve type-safety for the constants but they are treated as objects at runtime. So, the explanation of the objects above is applicable here too. The example given by OP in the question is:

    enum ColorsEnum {
        white = '#ffffff',
        black = '#000000',
    }
    

    Here ColorsEnum exists as an object at runtime, not as a type. This is because the types are erased once the Typescript code is compiled to Javascript. So, we need to invoke keyof typeof operators together as shown in the following code:

    type Colors = keyof typeof ColorsEnum
    
    let colorLiteral: Colors
    colorLiteral = "white"  // OK
    colorLiteral = "black"  // OK
    colorLiteral = "red"    // Error...
    

    That's it! Hope that helps.

    0 讨论(0)
提交回复
热议问题