-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Closed
Labels
Design NotesNotes from our design meetingsNotes from our design meetings
Description
Limiting Type Argument Inference from Binding Patterns
declare function f<T>(): T;
// Inference from binding patterns makes suspicious code like this work.
const { foo } = f(); // T: { foo: any }
// Motivation
declare function pick<T, K extends keyof T>(keys: K[], obj?: T): Pick<T, K>;
const _ = pick(["b"], { a: "a", b: "b", }); // T = "b"
const {} = pick(["b"], { a: "a", b: "b", }); // T = "b" | "a" (!?) (before fix)-
Arguably, these
anys should be implicitanys that get an error undernoImplicitAny. -
Inference from return types is generally suspicious, but binding patterns are kind of in the same domain.
-
Idea last time: binding patterns are useful for tuples, but not objects.
declare function f<T>(cb: () => T): T; const [e1, e2, e3] = f(() => [1, "hi", true]);
-
But then
declare function f<T>(): T; const [foo] = f(); // ?
-
-
So when does an object binding pattern provide a useful contextual type?
-
If the binding pattern has a default.
-
For literals it seems useless.
-
More complex defaults?
// Union of literals declare const oneOrZero: 0 | 1; const { b = oneOrZero } = f({ b: 0 }); // Contextually sensitive parameters function doSomething(x: string): string; const { func = doSomething } = f({ func: x => x });
-
-
Is this another level of inference priority?
- No, we just have two sets of inference contexts.
-
Conclusion: do it, get reviewed
Narrowing Type Parameters (un)Constrained to unknown
function deepEquals<T extends unknown>(a: T, b: T): boolean {
if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) {
return false;
}
if (Array.isArray(a) || Array.isArray(b)) {
return false;
}
if (Object.keys(a).length !== Object.keys(b).length) { // Error here
return false;
}
return true;
}- Given
someUnknown: unknown,typeof someUnknown === "objecttoday narrows toobject | null, but it doesn't narrow aT extends unknown - Added some changes to do this, then added some changes so
keyof NonNullable<T>→keyof Tsucceeds.- Needed this when you index into these types, you need to preserve new behavior on higher order
NonNullable<T>that you get (rather than on the previousT).
- Needed this when you index into these types, you need to preserve new behavior on higher order
- Have a parallel effort to improve how intersections with
{}operate - could unlock the same scenarios.- Ideally,
NonNullable<T>would just beT & {}, especially because intersections compose way better than conditional types. - With more aggressive
subtypesupertype reduction with{}, we can get there. - Also, need to be able to allow
unknownto be split betweenobject,null,undefined, and{}and remerged in CFA.
- Ideally,
- Can we do this without more aggressive subtype reduction?
- Actually, this isn't doing more aggressive subtype reduction, it's just supertype reduction.
- [[Above notes now reflect this]]
- Two approaches here, one focuses on the negative cases, the other on the positive cases.
- Both apply new narrowing
- Each PR has different "fallout", and has mitigations for the fallout.
- Conclusion: See if we can smush these changes together and take the best of each?
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Design NotesNotes from our design meetingsNotes from our design meetings