Home > Software engineering >  TypeScript check if a string is a key value in an interface
TypeScript check if a string is a key value in an interface

Time:11-15

const string = 'world';
const string2 = 'bar';
const string3 = 'test';

interface example {
  hello: 'world';
  foo: 'bar';
};

How can I check if a string is a key value in an interface?

So that in the example string and string2 would pass and string3 would throw an error?

Thanks!

CodePudding user response:

Consider this example:


const string = 'world';
const string2 = 'bar';
const string3 = 'test';

interface example {
    hello: 'world';
    foo: 'bar';
};


type IsAValue<Obj, Str extends string> = {
    [Prop in keyof Obj]: Str extends Obj[Prop] ? Str : never
}[keyof Obj]

type Result1 = IsAValue<example, 'world'> // world
type Result2 = IsAValue<example, 'bar'> // bar
type Result3 = IsAValue<example, 'test'> // never

IsAValue:

Prop - represents each key Obj - represents an object, in our case it is example interface.

This utility type iterates through each Obj (example) key and check whether Obj[Prop] extends second argument Str (string or string2). If yes - use Str as an object value, otherwise - use never. This line [keyof Obj] at the end of utility type obtains a union of all values in the object. If some value mathced Str we will get Str | never. Because never is a bottom type, and never is assignable to any type, union of Str | never just returns Str.

If you want to obtain just boolean from IsAValue, I mean true - value exists, false - not exists. You can just add conditional type, which will check whether results extends never or not:



const string = 'world';
const string2 = 'bar';
const string3 = 'test';

interface example {
    hello: 'world';
    foo: 'bar';
};


type IsNever<T> = [T] extends [never] ? true : false

type IsAValue<Obj, Str extends string> = IsNever<{
    [Prop in keyof Obj]: Str extends Obj[Prop] ? Str : never
}[keyof Obj]> extends false ? true : false

type Result1 = IsAValue<example, 'world'> // true
type Result2 = IsAValue<example, 'bar'> // true
type Result3 = IsAValue<example, 'test'> // false

Playground

If you want to throw an error, see this exmaple:


const string = 'world';
const string2 = 'bar';
const string3 = 'test';

type example = {
    hello: 'world';
    foo: 'bar';
};

type Values<T> = T[keyof T]

type Assert<Obj extends Record<string, string>, Key extends Values<Obj>> = Obj

type Result1 = Assert<example, 'world'> // true
type Result2 = Assert<example, 'bar'> // true
type Result3 = Assert<example, 'test'> // false

Playground

You might have noticed, that I have replaced interface example with type example. I made it purposely, because I have applied a constraint to Assert type. First argument should extend Record. Record is indexed type whereas interfaces in TS are not indexed by the default. More info you will find here and here

  • Related