No, the support for contextual narrowing of generics added in TypeScript 4.3, as implemented in microsoft/TypeScript#43183, doesn't do what you are expecting. This support narrows the type of the key from K to either "number" or "string", but it does not narrow the type of the type parameter K itself. Even if key is confirmed to be "number", the type parameter K is unchanged, and so the compiler cannot verify that 42 is assignable to Mapping[K]. It's frustrating.
There are some open issues, like microsoft/TypeScript#33014 and microsoft/TypeScript#27808 which propose ways by which the compiler might narrow K itself. But these have not been implemented yet.
If you'd like to rewrite get() so that it compiles with no errors and without giving up type safety, then you can do it like this:
function get<K extends keyof Mapping>(key: K): Mapping[K] {
return {
number: 42,
string: "hello"
}[key] // okay
}
Your return type is a generic indexed access type Mapping[K]. The compiler can verify that if you index into a value of type Mapping with a key of type K, you'll get a value of type Mapping[K], so that's why the above rewritten code works.
This relies on getting ahold of a value of type Mapping. Sometimes you can't or don't want to do this because it forces you to eagerly pre-calculate the results for every possible input when you don't want to. If so, you could implement getters instead so that the Mapping object is lazily computed:
function get<K extends keyof Mapping>(key: K): Mapping[K] {
return {
get number() { return 42 },
get string() { return "hello" }
}[key]
}
Playground link to code