Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can one constrain a typescript generic to be an indexer?

In typescript you can index into an object using only a string, number or symbol.

I want to have a generic that allows the generic parameters to be used as indexes.

function foo<T extends number|string>(a: T): void {
    let x: any = {};
    x[a] = 42; // Error: an index expression argument must be of type...
}

If I cast to a number|string, it compiles:

function foo<T extends number|string>(a: T): void {
    let x: any = {};
    let i: number|string = a;
    x[i] = 42; // OK
}

So the type checker is smart enough to know that if T extends number|string then I can assign it to a number|string (which I can then can use to index into an object) but for some reason it won't let me index with a T directly.

Is there a way to specify in the generic constraint that the type passed can be used as an indexer?

Note: I'm using a generic and not number|string directly because I want to constrain the generic to accept only values from a specific enum or a string literal union.

Edit: A more compelling example (and closer to what I'm really trying to do), is this:

// The following line fails to compile even though Key is number | string
type Dictionary<Key extends number | string, Value> = { [index: Key]: Value }; 

// Had it compiled I would expect the following behaviour
type Answer = "yes" | "no" | "maybe";
let grades = {} as Dictionary<Answer, number>;  

grades["yes"] = 10;
grades["nope"] = 5; // Should be an error "nope" is not assignable to Answer
like image 615
Motti Avatar asked Nov 30 '16 12:11

Motti


People also ask

What are constraints in TypeScript?

Type constraints are a very powerful way to supercharge your typescript codebases. For each generic type variable that you'd like to constrain, you would append the extends SomeTypeName to the definition. Example: const myFn = <T extends JsonValues, U extends FinancialData>() => {...}

What TypeScript keyword is used to apply a constraint to a generic type parameter?

Use extends keyword to constrain the type parameter to a specific type.

What encloses a generic in TypeScript?

In TypeScript, the syntax for generics is rather simple. We use angle brackets to enclose the generic type-parameter.

What is generic TypeScript?

TypeScript Generics is a tool which provides a way to create reusable components. It creates a component that can work with a variety of data types rather than a single data type. It allows users to consume these components and use their own types.


1 Answers

// The following line fails to compile even though Key is number | string
type Dictionary<Key extends number | string, Value> = { [index: Key]: Value };

Use in keyword:

type Dictionary<TKey extends number | string, TValue> = {
  [key in TKey]: TValue
};
like image 64
Mirza Brunjadze Avatar answered Sep 25 '22 22:09

Mirza Brunjadze