-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
Bug Report
Per the docs on conditional types, deferred conditionals are expected to be resolved once sufficient context is provided (e.g. at a call-site) such that the compiler can evaluate a single branch of the conditional.
It appears that when the conditional type is in the value position of a mapped type, this does not occur.
🔎 Search Terms
deferred conditional types, mapped types
🕗 Version & Regression Information
- This is the behavior in every version I tried (all versions on playground), and I reviewed the entire FAQ
⏯ Playground Link
Playground link with relevant code
💻 Code
interface Targets<A> {
left: A
right: A
}
type Target = keyof Targets<any>
type Result<F extends Target, A> = Targets<A>[F]
type LR<F extends Target, L, R> = [F] extends ["left"] ? L : R
interface Ops<F extends Target> {
_f: F
str: Result<F, string>
num: Result<F, number>
lr<I, O>(a: Result<F, I>, o: Result<F, O>): Result<F, LR<F, I, O>>
dict: <P>(p: {[k in keyof P]: Result<F, P[k]>}) => Result<F, P>
}
const left: Ops<"left"> = {} as any
const right: Ops<"right"> = {} as any
const ok = <F extends Target>(at: Ops<F>) => ({lr: at.lr(at.str, at.num)})
const orphaned = <F extends Target>(at: Ops<F>) => at.dict(ok(at))
const leftOk = ok(left)
const leftOrphaned = orphaned(left)
const rightOk = ok(right)
const rightOrphaned = orphaned(right)🙁 Actual behavior
Note that the assignments of leftOk and rightOk properly evaluate the conditional type to the branches dictated by the respective arguments passed to ok, with leftOk taking type {lr: string} and rightOk taking type {lr: number}.
However with the same information available (same exact arguments, just passed through one extra layer of call-stack, and a mapped type), leftOrphaned and rightOrphaned do not evaluate the conditional type, both remaining at type {lr: LR<F, string, number>} despite F being determined by the respective arguments passed to orphaned.
🙂 Expected behavior
The types of leftOrphaned and rightOrphaned should match the types of leftOk and rightOk respectively. Application of a type argument to a type parameter (in this case "left" or "right" to F) should apply to sub-expressions in a function body, and those applications should hold under a mapped type.