Reputation: 4519
I am trying to create an object where I would like to enforce the keys, but am happy to let typescript deduce the types of the values. A quick example is
const fooVals = {
a: null,
b: null,
c: null,
e: null,
}
type TfooVals = typeof fooVals
type JustKeysOfFooVals = { [key in keyof TfooVals]: any};
// TS deduces correct types of foo1Vals but does not let me know e is missing
const foo1Vals = {
a: 'string',
b: 10,
c: Promise.resolve('string') ,
// e: () => { console.log('bar') }
}
// lets me know 'e' is missing, but makes types any
const foo2Vals: JustKeysOfFooVals = {
a: 'string',
b: 10,
c: Promise.resolve('string') ,
e: () => { console.log('bar') }
}
Is this possible?
Upvotes: 1
Views: 349
Reputation: 330216
I'd recommend using a generic helper function which constrains its input to a subtype of JustKeysOfFooVals
and just returns its input without widening it:
const justKeysOfFooVals = <T extends JustKeysOfFooVals>(t: T)=>t;
You'd then use it like this:
const foo1Vals = justKeysOfFooVals({
a: 'string',
b: 10,
c: Promise.resolve('string') ,
// e: () => { console.log('bar') }
}); // error! property 'e' is missing
const foo2Vals = justKeysOfFooVals({
a: 'string',
b: 10,
c: Promise.resolve('string') ,
e: () => { console.log('bar') }
}); // okay
foo2Vals.e(); // okay
You get warned about missing keys and it doesn't forget about the value types. Hope that helps; good luck!
Update: the helper function may disable excess property checks. If you need those (and you might not, after all a value {a: "a", b: "b"}
is a perfectly valid instance of type {a: string}
), then you can use another generic constraint to simulate exact types:
type Exactly<T, U> = T & Record<Exclude<keyof U, keyof T>, never>;
const justKeysOfFooVals = <T extends Exactly<JustKeysOfFooVals, T>>(t: T)=>t;
const foo3Vals = justKeysOfFooVals({
a: 'string',
b: 10,
c: Promise.resolve('string') ,
e: () => { console.log('bar') },
f: 1 // error!
}); // number is not assignable to never
Good luck again!
Upvotes: 2