There is currently a limitation in TypeScript where a computed property key whose type is not a single string literal type is widened all the way to string. See microsoft/TypeScript#13948 for more information. So, for now, in order to use a computed property with a type narrower than string you will need to do something a little unsafe like use a type assertion.
One reason there hasn't already been a fix for this issue is that you can't simply say that {[k]: v} is of type Record<typeof k, typeof v>. If typeof k is a union type (or if it is a generic type, which might end up being specified as a union type), then Record<typeof k, typeof v> has all the keys from the union type, whereas the true type of {[k]: v} should have just one of those keys.
You would run into the same problem with your pick() implementation. The type of pick(obj, key) is not necessarily Pick<T, K>, precisely because K might be specified with a union type. This complication makes things a bit harder to deal with.
The "right" type for pick(obj, key) is to distribute Pick<T, K> across unions in K. You could either use a distributive conditional type like K extends keyof T ? Pick<T, K> : never or a distributive object type like {[P in K]-?: Pick<T, K>}[K].
For example, if you have the following,
interface Foo {
a: number,
b: number
}
const foo: Foo = { a: 0, b: 1 }
const someKey = Math.random() < 0.5 ? "a" : "b";
// const someKey: "a" | "b"
const result = pick(foo, someKey);
You don't want result to be of type Pick<Foo, "a" | "b">, which is just Foo. Instead, we need to define pick() to return one of the distributive types above, and we need to use a type assertion to do it:
function pick<T, K extends keyof T>(obj: T, key: K) {
return { [key]: obj[key] } as K extends keyof T ? Pick<T, K> : never
}
And that results in the following result:
const result = pick(foo, someKey);
// const result: Pick<Foo, "a"> | Pick<Foo, "b">
So result is either a Pick<Foo, "a"> or a Pick<Foo, "b">, as desired.
Playground link to code
keyis generalized tostring? Or do you want to know how to implementpickwithout using type assertions?anyhides possible errors. Usingas ...hides possible errors. I wonder if it is possible to implementpickwithout type assertions and if not why. To me it looks like all the information to the compiler should be available.pick({a: 0, b: 1}, Math.random()<0.5?"a":"b")should presumably produce a value of type{a: number} | {b: number}, but your version claims to produce{a: number, b: number}. My suggestion here would be this. If that answers your question I can write up an answer; if not,what am I missing?pick<Foo, "a" | "b">({a: 0, b: 1}, "a"), which might be a little bit simpler to understand.