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

Implementation ...Record<T> #2785

Closed
timotheeguerin opened this issue Jan 10, 2024 · 1 comment · Fixed by #2920
Closed

Implementation ...Record<T> #2785

timotheeguerin opened this issue Jan 10, 2024 · 1 comment · Fixed by #2920
Assignees
Milestone

Comments

@timotheeguerin
Copy link
Member

timotheeguerin commented Jan 10, 2024

Implementation for design #2442

1. Make & intersection actually mean intersection of properties

This means the following code should be an error because we cannot intersect string and int32(what we do if you intersect explicitly)

alias MergedBad = Record<int32> & {name: string};
                                 ^ cannot intersect record ...

2. Allow spreading Record<T>

This would make & and ... meaning diverge:

  • & would mean intersection of types(as described above) and so would merge the properties and if types are not able to be merged it would be an error/resolve to never
  • ... would mean spread the properties and so would add extra properties. This means if you spread extra properties then those get added, if properties name conflict then it is an error. If you spread a Record, it describe the remaining properties.
model WithSpread {
  name: string;
  age: int32;
  ...Record<string>;
}

The code above would mean you have a model with a property name of type string, a property age of type int32 and any other property of type string.

Multiple ...Record<T>

As ... allows multiple instance in the same model it is possible to have multiple ...Record<T> in the same model. This would mean that each ...Record<T> would be a union of each other.
If you spread Record<string> and Record<int32> it means all extra properties could either be a string or int32. You are basically saying I am spreading some string properties and some int32 properties.

model WithSpread {
  name: string;
  enabled: boolean;
  ...Record<string>;
  ...Record<itn32>;
}

// is equivalent to
model WithSpread {
  name: string;
  enabled: boolean;
  ...Record<int32 | string>;
}

Difference with is Record<T>(or extends Record<T>)

model Foo is Record<T> will be defining the indexer for Foo which means all properties added to Foo must be of type T. This also means that Foo can be used when a constraints of type Record<T> is needed.

However when spreading Record<T> it will not be setting the indexer as some properties could technically be of a different type.

model Template<T extends Record<string>> {t: T}

model WithSpread {
  age: int32;
  ...Record<string>;
}
model WithIs is Record<string> {
  string: string;

}

model Test {
  good: Template<Record<string>>;
  goodWithIs: Template<WithIs>;
  bad: Template<WithSpread>;
        ^ error WithSpread is not assignable to Record<string>
}

Technically having this would also pass the constraint above as all the extra properties are also assignable to the constraint.


```tsp
model WithSpread {
  name: string;
  ...Record<string>;
}

is or extends a model with ... Record<T>

At this point it should be an error if you try to define a property that is incompatible with the base model spread indexer.

Other items

  • Add docs
@markcowl
Copy link
Contributor

est: 8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants