-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Type inference for homomorphic mapped types #12528
Conversation
# Conflicts: # src/compiler/checker.ts
@ahejlsberg That is a lot of code - could you explain what exactly the before/after is here? What wasn't working before? What works with this change? |
@DanielRosenwasser I've added a bunch of comments to the examples. |
This is just incredible. I thought about just using the reaction button, but I feel like thats not enough to express how awesome I believe this is. I previously thought that some dynamic aspects of JS were unbeatable by any type system, but now I'm having serious doubts. The set of things that cannot be expressed with TypeScript's type system is shrinking very quickly! Thanks @ahejlsberg - truly amazing work on mapped types. |
@ahejlsberg is this PR available already on The code below should ADD a location to every value in the object it is passed: type Box<T> = { value: T }
type Loc = { loc: number }
function addLocations
<T,
B extends Block<Box<T>>,
O extends { [ K in keyof B ]: B[K] & Loc }>
( b: B ): O {
const out: O = {}
for ( const key in b ) {
out[key] = { value: b[key].value, loc: 0 }
}
return out
}
const bs = { age: { value: 5 } }
const bts = addLocations(bs) |
@stevekane Just merged the PR, it will be in tonight's nightly. The problem in your example is that you're using constrained type parameters instead of actual types. As a rule of thumb, constraints are checked after type inference, but do not actually participate in type inference. For that reason, the compiler doesn't "see" the relationships you're trying to express. You should instead have the minimal number of type parameters and express the relationships in the actual parameter types. For example, the following works (when you use a branch with this PR): type Box<T> = { value: T }
type Loc = { loc: number }
type Boxified<T> = {
[P in keyof T]: Box<T[P]>
}
type BoxLocified<T> = {
[P in keyof T]: Box<T[P]> & Loc
}
function addLocations<T>(b: Boxified<T>): BoxLocified<T> {
const result = {} as BoxLocified<T>;
for (const key in b) {
result[key] = { value: b[key].value, loc: 0 };
}
return result;
} |
@spion Much appreciate the kind words! |
Beautiful @ahejlsberg. Thanks for taking the time to provide that example and the valuable explanation. I am now fully up-and-running w/ this type-safe webgl library and will update the issues I have opened with comments saying as much. Very fine work here. Your speed and clarity of implementation is impressive. |
This PR introduces deeper type inference for homomorphic (structure preserving) mapped types. In particular, with this PR we now have the ability to infer unmapped forms of mapped types: When inferring from a type
S
to a homomorphic mapped type{ [P in keyof T]: X }
, we attempt to infer a suitable type forT
that satisfies the mapping expressed byX
. We construct an object type with the same set of properties asS
, where the type of each property is computed by inferring from the source property type toX
for a synthetic type parameterT[P]
(i.e. we treat the typeT[P]
as the type parameter we're inferring for).