Trying to answer this question has left me with more questions than answers. I'll let the code speak for itself:
type MyAnimals = { name: "dog", fleas: 2 } | { name: "cat" }
function GetKeys<T extends MyAnimals>(animal: T) {
for (let k in animal) {
let sansAssertion = k;
sansAssertion = 'name'; // err -> whyyyyy?
sansAssertion = 'fleas'; // err -> I get it...
sansAssertion = 'hohoho'; // err -> yeah, that's Bullsh1t
const thisIsConst = 'name' as const; // the type is 'name', not string, FOR SURE
sansAssertion = thisIsConst; // err -> dafuck? Still not?
let withAssertion = k as keyof T;
withAssertion = 'name'; // works -> aha!
withAssertion = 'fleas'; // err -> yeah
withAssertion = 'hohoho'; // err -> Bullsh1t
let letsBeExplicit: Extract<keyof T, string> = 'name'; // err -> okay, expected, by now.
// Can I assign ANYTHING to this??
type SpelledOut = Extract<('name' | 'fleas') | 'name', string>
let noGeneric: SpelledOut = 'name'; // works -> yup, okay, it's not _just_ the Extract that's broken
type LessSpelledOut = Extract<keyof ({ name: string, fleas: number} | { name: string }), string>;
let fromKeys: LessSpelledOut = 'name'; // works -> yeees
fromKeys = 'fleas' // err -> yeees
}
}
In case my question isn't clear by now: TypeScript is clearly able to understand the keyof T
type and acts as expected. keyof T
contains a union of a bunch of different strings (only one, in this case). It would be expected, that Extract<ABunchOfStrings, string> === ABunchOfStrings
, yet it is not if ABunchOfStrings
comes from a generic type keyof T
. Whyyyyy?