Open KyleDavidE opened 1 year ago
As far as I can tell, the issue here is that whenever T satisfies U
compiles, it always means just T
; any type information in U
is simply discarded. It's a simple rule to understand, but I think it could be made more useful while still retaining the spirit of the original rule.
I have a similar case where I think it would be better for satisfies
to be more than just an assertion. In the example below, the inferred type of foo
is {a: string}
, but I really want it to be {a: string: b?: string}
. In other words, I want satisfies
to change the type such that if b
is present, its type is string
.
interface Partial {
a: string;
b?: string;
}
function foo() {
return [{ a: "a" }] satisfies Struct;
}
What I'm suggesting is almost like making T satisfies U
implicitly mean T satisfies U as T & Partial<U>
, but it breaks when the interesting type is nested in another type like an array: T[] satisfies U[] as T[] & Partial<U[]> /*WRONG*/
; it should be smart enough to apply Partial
recursively, like this: T[] satisfies U[] as T[] & Partial<Partial<U>[]>
.
@johnw42 that behavior (or something akin to it) was discussed in the original satisfies
proposal and rejected due to other bad side effects
I don't understand why x as unknown === y
isn't a good solution tbh
Suggestion
🔍 Search Terms
comparison, satisfies, equality, overlap, equals
✅ Viability Checklist
My suggestion meets these guidelines:
⭐ Suggestion
Add the ability for satisfies to hint a possible overlapping type for equality. Eg given
x: X
andy: Y
ifX extends S
andY extends S
thenx satisfies S === y
should type check even thoughx == y
does not.Right now, typescript has a check for type overlaps on equality. This catches cases where you compared the wrong values but is sometimes wrong if you have two interfaces, since classes can implement more than one interface. Often
📃 Motivating Example
💻 Use Cases
It's a bit hard to come up with a compact real-world example of this coming up because it often happens as a consequence of the structure of large codebases. I have mostly seen it come up when comparing an abstract class implementation of an interface with an instance of a more-specific interface, eg:
In the meantime, using
as
casting as a workaround works, but in general it would be nice to avoid using type-unsafe casting in places where it isn't need. An alternative to this would be an explicit upcasting operator.