The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
#Can you implement generics with upper bound/`satisfies` somehow
1 messages · Page 1 of 1 (latest)
@unreal oracle Here's a shortened URL of your playground link! You can remove the full link from your message.
Preview:```ts
// This is just an interface used for testing
interface Abc { a: boolean, b: number, c: string }
// This type converts all props to optional booleans, it's non-recursive to make the example simpler
type AsTrues<T> = { [K in keyof T]?: boolean }
// You can hover here and see that we get { a?: boolean, b?: boolean, c?: boolean }
...```
You can choose specific lines to embed by selecting them before copying the link.
satisfies and extends work in the same direction
U extends T, U must be a subtype of T
v satisfies T, v's type must be a subtype of T
are you looking for something more like java's super?
ts doesn't have that currently
extends(java & ts)/satisfies(ts) give upper bounds, super(java) gives a lower bound
looks like you want more granular generics here actually
moving the Filter generic to the method solves the issue by letting the inferred type be more specific to the call
i don't think you need Props entirely, either
Preview:```ts
// This is just an interface used for testing
interface Abc { a: boolean, b: number, c: string }
// This type converts all props to optional booleans, it's non-recursive to make the example simpler
type AsTrues<T> = { [K in keyof T]?: boolean }
// You can hover here and see that we get { a?: boolean, b?: boolean, c?: boolean }
...```
You can choose specific lines to embed by selecting them before copying the link.
@unreal oracle
I wish I could see a diff 🙂
hmm, so you basically push the generics and constraints down to the method level for it to work
Thanks! I was really hoping it would be possibly somehow. I'll try it out with my larger real case now.
theoretically you could make it yourself, but manual diff here we go
- class WithObject<E, Filter extends AsTrues<E>> {
+ class WithObject<E> {
constructor(private e: HasConstructor<E>) { }
- withFilter(filter: Filter) {
+ withFilter<Filter extends AsTrues<E>>(filter: Filter) {
return new ObjectWithFilter(this.e, filter)
}
}
- class ObjectWithFilter<E, Filter extends AsTrues<E>, Props extends PickWhereTrue<E, Filter>> {
+ class ObjectWithFilter<E, Filter extends AsTrues<E>> {
constructor(private e: HasConstructor<E>, private filter: Filter) { }
- doSomething(props: PickWhereTrue<E, Filter>) {
+ doSomething(props: Props) {
console.log(this.e, this.filter, props)
}
}
damn, you are like chatGPT 🙂
yeah, so the generic can be inferred in each individual method call
please don't compare me to something like that
or anyone, for that matter
it was supposed to be a compliment 🙂
it really isn't
as in "you have a lot of knowledge and you are very responsive"
chatgpt doesn't have any knowledge
ok, i think we are getting off topic now.
it parrots things it sees without understanding them, humans actually have to understand them
so yeah please don't compare anyone to chatgpt, it isn't a compliment
anyway, thanks. I will see if my larger example which this was based on can be fixed by applying this method now.
so i guess in general it is better to move the generics and constraints down to the function level if you have a generic class or interface.
not really
it isn't an "in general" thing
it's just, what is the generic related to?
data of the entire object? that goes on the class
a specific operation? that goes on the specific method
if something should be specific to an operation, like here, and you put it on the class, that makes the generic apply to all operations of that type, meaning you can't get individually linked operations anymore, they're all of the same type
that's a really confusing explanation hold on
Preview:```ts
class ExampleA {
identity<T>(v: T): T {
return v;
}
}
class ExampleB<T> {
identity(v: T): T {
return v;
}
}
const a = new ExampleA(); // T doesn't exist yet
const b = new ExampleB(); // T is unknown by default
const resultA = a.identity(0); // correctly infers T
...```
You can choose specific lines to embed by selecting them before copying the link.
no, I think I already see what you are saying
since it's the same generic in ExampleB, b.identity(0) and b.identity(1) have to yield the same type
Thanks you got me unstuck. Now I think I got to a point where my proof of concept seems to be demonstrated.
Do you btw know if there's a way to improve the developer experience for the case where a mapped type maps to {a: number} | ClassWithMembers without that being unioned together into a mess of attributes that the ClassWithMembers supports.
I don't want the intellisense to suggest setting up a value that has the props of ClassWithMembers. Instead you should instanstiate the class directly if that's what you wanted to use.
idk about that, sorry
ClassWithMembers as a type is really just an interface in the shape of the class
yeah, it would be nice to treat class instances as opaque types that you can't create by setting them up with their individual members for this case though
i think that question would warrant a separate thread
i don't know anything about that, sorry
yeah i agree. thanks.
!resolved