The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
#Can I infer record key type from value type?
19 messages · Page 1 of 1 (latest)
I'm hesitant to say it's impossible, maybe someone else finds a solution, but I don't think you'll be able to make this one work.
For one, you're asking a lot of the type system
Secondly, you're running into the correspondence problem
Yeah I don't think you can do it without casting at all, the problem is that TS allows excess properties so Object.entries is inherently unsafe.
And what you are doing is just reflection in other languages.
But if you are fine with casting, usually you want to use Object.fromEntries rather than reducing.
Preview:```ts
type B = Record<PropertyKey, string | number>
function test<TB extends B>(i: TB) {
const result = Object.fromEntries(
Object.entries(i).map(([key, value]) => [
key,
typeof value === "string"
? new RegExp(value)
: new Date(value)
...```
You can choose specific lines to embed by selecting them before copying the link.
I'm trying to avoid casting. But of course, I'll do that when required
I'm using keyof from generic type. Isn't it safe?
!:unsafe-keys
`!retsam19:unsafe-keys`:
Since TS allows objects to have extra properties not specified in the type, it doesn't assume that all the keys on the type are the only keys on the object. This means that Object.keys returns string[] not a specific type, and for(const key in obj), key is string, (not keyof typeof obj).
If you wish to assume otherwise, this utility is often helpful:
// A signature for `Object.keys` that assumes the only keys are the ones indicated by the type
const unsafeKeys = Object.keys as <T>(obj: T) => Array<keyof T>;
An example:
function doStuff<T extends object>(obj: T) {
const keys = Object.keys(obj)
}
const fooBarBaz = { foo: 'hello', bar: 'world', baz: 42 }
const fooBar: { foo: string; bar: string } = fooBarBaz
doStuff(fooBar)
In the case of passing in fooBar, if you type keys as keyof T then that means it would be 'foo' | 'bar', when in reality it's actually 'foo' | 'bar' | 'baz'.
(Just FYI, there's also a snippet for this)
!*:unsafe-keys-example
`!retsam19:unsafe-keys-example`:
An example where assuming Object.keys(x) returns keyof (typeof x) is unsafe:
type XY = {
x: string,
y: string,
}
function louder(obj: XY) {
Object.keys(obj).forEach(key => {
// If key is "x" | "y", then this compiles:
console.log(obj[key].toUpperCase());
})
}
const obj = { x: "x", y: "y", zero: 0};
louder(obj); // compiles, because extra properties are allowed... but crashes at runtime
Make sense. But what am I doing is infer result from input type from user. So, the developers should always prefer pass the correct type info for correct result