Home > front end >  Is it possible to specify that a string should match an interface property name in TypeScript?
Is it possible to specify that a string should match an interface property name in TypeScript?

Time:01-22

I have an interface like this for example:

interface Account {
    email: string;
    enabled: boolean;
}

And I would like to create a method that returns a default value for these fields. So I have something like this:

function defaultValue(propName:string) {
    if (propName === 'email') return '';
    if (propName === 'enabled') return true;
}

It works well however if any property of Account changes, the defautValue() method would still compile even though it's now broken.

So I'm wondering if I can specify that propName should have a type like property name of Account? Or is there any other good pattern I can use to enforce type checking in that situation?

CodePudding user response:

You can use a function to assert the variable is never by the end of the if statements and keyof T to get a union of keys:

function assertNever(v: never): never {
  throw new Error("Should never happen!");
}

function defaultValue(propName:keyof Account) {
    if (propName === 'email') return '';
    if (propName === 'enabled') return true;
    assertNever(propName);
}

Playground Link

You can also improve the caller experience by making the function generic, but this will mean some type assertions in the implementation.


function defaultValue<K extends keyof Account>(propName:K): Account[K] {
    if (propName === 'email') return '' as Account[K];
    if (propName === 'enabled') return true as Account[K];
    assertNever(propName);
}

let email = defaultValue('email') // string
let enabled = defaultValue('enabled') // boolean

Playground Link

CodePudding user response:

You can do this by adding a more specific string type to the propName parameter. Then, use a switch statement to have smart type inference that you are correctly returning a value that matches the return type for each possible branch in the function.

interface Account {
    email: string;
    enabled: boolean;
    shouldError: boolean;
}

function defaultValue(propName: keyof Account): Account[keyof Account] { // shows error for not always returning value
    switch (propName) {
        case "email":
            return "";
        case "enabled":
            return true;
        // uncomment this code to fix the error
        // case "shouldError":
        //     return true;
    }
}

TypeScript Playground Link

  •  Tags:  
  • Related