Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-type subtyping and type pairing #159

Closed
kaleidawave opened this issue Jun 3, 2024 · 3 comments
Closed

Non-type subtyping and type pairing #159

kaleidawave opened this issue Jun 3, 2024 · 3 comments
Labels
checking Issues around checking feedback-needed Extra attention/consensus is needed

Comments

@kaleidawave
Copy link
Owner

Currently type pairing (/generic inference) is done during subtyping

For

function x<T>(t: T): T { return t }

x(5)

During subtyping in the function invocation: the 5 argument is matched against the T parameter.

The current implementation first checks whether 5 meets the constraint of T. It is any in this case and thus passes.

Then it adds (T, 5) to the current state of subtyping, which effectively implies that T=5 and during return substitution it substitutes any T references with literal type 5.

This currently works very well. Additionally it:

  • Records the depth (how many objects it goes through) to pick a more accurate generic (this is TS behaviour)
  • Stores the pairs sequentially. At or branches, if something fails then it discards the results. See cancelled generics

This works when doing TypeId <=> TypeId comparison. The problem is for the following

  • "Hello Ben" extends `Hello ${infer T}` ? T : .... The string comparison isn't implemented yet. But when it does it needs to create a (T, "Ben") pair. The problem is that subtype cannot create new types (it doesn't have access to a &mut TypeStore (only the non mutable reference).
  • Same for array slices
  • For property keys in mapped types. The comparison is between PropertyKey (again not always a type can be PropertyKey::String) so would have to create some type thing here.

Possible implementation 1

Move the calculation into the type reference. Aka something like infer T like `Hello ${string}` ? remove first 6 characters from T :... and do the logic internally.

Doesn't solve mapped type property key issue

Possible implementation 2

Make the paired result into a enum. Little bit scared that adds overhead to core item for the sake of TS consistency? Started on enum PairRHS { Type(TypeId), PropertyKey(PropertyKey<'static>), SliceOf(...) } in #146 but paused to get it merged. Maybe should have another go?

@kaleidawave kaleidawave added feedback-needed Extra attention/consensus is needed checking Issues around checking labels Jun 3, 2024
@kaleidawave kaleidawave changed the title Non type type pairing Non-type subtyping and type pairing Jun 15, 2024
@kaleidawave
Copy link
Owner Author

Subtyping

Subtyping of

  • type <-> slice of string
  • type <-> property key

and the combination isn't too difficult. Might require some abstraction away from the type_is_subtype function, a CompareToString type etc

array slices are similar, but that is comparing slices of TypeIds rather than chars

Pairing / generic inference / contributions

This is the harder issue

For mapped types with as, this requires pairing types during property lookup

export type Mapped<T> = {
    [P in (keyof T & string) as `property_${P}`]: T[P]
}

function func(p: Mapped<{ a: string }>) {
    p.property_a
}

Adding contributions to property lookup matching is quite complex.

Desugaring

instead maybe

export type Mapped<T> = {
    [P in (keyof T & string) as `property_${P}`]: T[P]
}
// under synthesis it could be synthesised as the following. Where the `P` is moved to the RHS
export type Mapped<T> = {
    [P in (keyof T & string) as `property_${keyof T & string}`]: T[Slice<P, 0, 8>]
}

Where Slice is an internal type.

This could be implemented quite simply here because the root would be just P type argument

return Some(if let Some((_parameter, _)) = key.mapped_generic_id(types) {
// Logical::Implies {
// on: Box::new(pure),
// antecedent: GenericArguments::ExplicitRestrictions(todo!(
// "want TypeArgument::PropertyKey 😀"
// )),
// }
crate::utilities::notify!(
"TODO to set property key as a type argument?"
);
pure

@kaleidawave
Copy link
Owner Author

Managed to get type_is_subtype into key_matches.

The next part is to try and get any matches through. There are two problems

  1. get_property_unbound needs to pass the slice through. To do this it could do through Logical::Implies. However that requires changing either GenericChainLink, GenericArguments or ExplicitTypeRestrictions. Don't like GenericArguments or ExplicitTypeRestrictions because that is PartiallyAppliedGenerics and that is a lot simpler using TypeId. Also that is perfectly fine because PartiallyAppliedGenerics is created with &mut TypeStore. GenericChainLink is a little harder.
  2. Similarly during subtyping, there is no &mut TypeStore, so getting these arguments out needs a bit of work :/

@kaleidawave
Copy link
Owner Author

Pretty much done in #157

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
checking Issues around checking feedback-needed Extra attention/consensus is needed
Projects
None yet
Development

No branches or pull requests

1 participant