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
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>() => {...}
Use extends keyword to constrain the type parameter to a specific type.
In TypeScript, the syntax for generics is rather simple. We use angle brackets to enclose the generic type-parameter.
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.
// 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
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With