Reputation: 126
Apologies for the title, having trouble making it clearer.
Let's say I have a class Person
:
class Person {
name: string;
age: number;
}
And another class FormControl
:
class FormControl<T> {
constructor(
public key: keyof T,
public value: any // What should value's type be?
) {}
}
What I'm trying to achieve is to create an instance of the FormControl
class as such...
new FormControl<Person>('name', 'Bob');
Where the key
property must be a key of T
, and the value
property must be the corresponding type of the given key of T
.
Simply having keyof T
works great for the key
field, but I am not sure how to approach the type for the value
field.
Is this possible with Typescript? Or is there another way to achieve something similar to this?
Upvotes: 1
Views: 67
Reputation: 126
Thank you for the great answer superhawk610!
Rather than using that factory, I am using another class's constructor to group the FormControl
instances together, which also removes the need to specify both generics:
class FormControlGroup<T> {
constructor(
public controls: FormControl<T, any>[]
) {}
}
Not sure if I should be using any
as the second generic, but it seems to work.
FormControlGroup
in use:
new FormControlGroup<Person>([
new FormControl('name', 'Bob'),
new FormControl('age', 'eleventeen') // TypeError - expected number
])
Upvotes: 0
Reputation: 2653
TypeScript has property access notation Index[Key]
for retrieving the type of the value specified for the corresponding key in an index type:
type PersonName = Person['name']; // string
type PersonAge = Person['age']; // number
You can use this to constrain the constructor like so:
class FormControl<T, K extends keyof T> {
constructor(
public key: K,
public value: T[K],
) {}
}
Unfortunately, TS doesn't allow you to specify only a subset of generic type parameters, so you'll have to specify both generics:
new FormControl<Person, 'name'>('name', 'Bob');
new FormControl<Person, 'age'>('age', 'five'); // TypeError - expected number
You can work around this using a curried function, but the syntax is a bit awkward:
const formControlFactory = <T extends any>() =>
<K extends keyof T>(key: K, value: T[K]) =>
new FormControl(key, value);
const personFCFactory = formControlFactory<Person>();
personFCFactory('name', 'Bob');
personFCFactory('age', 'five'); // TypeError - expected number
You can read the discussion on specifying only a subset of generic type parameters here.
Upvotes: 1