| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
There was a problem hiding this comment.
One small question/comment.
Sorry, something went wrong.
| // to a union type A | B, we produce { [P in keyof A]: X } | { [P in keyof B]: X }. Furthermore, for | ||
| // homomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a | ||
| // union type A | undefined, we produce { [P in keyof A]: X } | undefined. | ||
| // For a momomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping |
There was a problem hiding this comment.
Typo:homomorphic
Sorry, something went wrong.
| const elementTypes = map(tupleType.typeArguments || emptyArray, (_, i) => | ||
| instantiateMappedTypeTemplate(mappedType, getLiteralType("" + i), i >= minLength, mapper)); | ||
| const modifiers = getMappedTypeModifiers(mappedType); | ||
| const newMinLength = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : |
There was a problem hiding this comment.
Might be worth extracting this calculation into a function.
Sorry, something went wrong.
| if (isMappableType(t)) { | ||
| return instantiateAnonymousType(type, createReplacementMapper(typeVariable, t, mapper)); | ||
| const replacementMapper = createReplacementMapper(typeVariable, t, mapper); | ||
| return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) : |
There was a problem hiding this comment.
isOptional doesn’t have any meaning for arrays, does it? Why not pass false if that’s true?
Sorry, something went wrong.
There was a problem hiding this comment.
The isOptional indicates whether a -? modifier on the mapped type should strip undefined from the source type. We want to do that when the source type originates in an optional property, but we also want to do it when the source type is an array element type.
Sorry, something went wrong.
|
First: this is awesome. Next: I know there were questions around still being able to map arrays/tuples like objects if the need arises. Below are some constructs that seem to allow that (PromisifySomeKeysTuple and PromisifyIntersectionTuple); the question is: are they intended or unintended? type PromisifyNormal<T> = { [K in keyof T]: Promise<T[K]> }
type PromisifyNormalObject = PromisifyNormal<{ a: string, b: number, c: boolean }>
// {a: Promise<string>, b: Promise<number>, c: Promise<boolean>}
type PromisifyNormalTuple = PromisifyNormal<[string, number, boolean]>;
// [Promise<string>, Promise<number>, Promise<boolean>]
type PromisifySomeKeys<T, KT extends keyof T = keyof T> = { [K in KT]: Promise<T[K]> }
type PromisifySomeKeysObject = PromisifySomeKeys<{ a: string, b: number, c: boolean }>
// {a: Promise<string>, b: Promise<number>, c: Promise<boolean>}
type PromisifySomeKeysTuple = PromisifySomeKeys<[string, number, boolean]>;
/*
type PromisifySomeKeysTuple = {
[x: number]: Promise<string | number | boolean>;
"0": Promise<string>;
"1": Promise<number>;
"2": Promise<boolean>;
length: Promise<3>;
...
}
*/
type PromisifyIntersectionTuple = PromisifyNormal<[string, number, boolean] & { randomProp: 1234 }>;
/*
type PromisifyIntersectionTuple = {
[x: number]: Promise<string | number | boolean>;
"0": Promise<string>;
"1": Promise<number>;
"2": Promise<boolean>;
length: Promise<3>;
...
randomProp: Promise<1234>;
}
*/ |
Sorry, something went wrong.
|
This is really cool and I actually just asked if this was possible in 3.0 here. I have had a play with this with the insiders build and it does exactly what I want: class Maybe<T>{}
type MaybeTuple = [Maybe<string>, Maybe<number>, Maybe<boolean>];
type MaybeType<T> = T extends Maybe<infer MaybeType> ? MaybeType : never;
type MaybeTypes<T> = {[P in keyof T]: MaybeType<T[P]>};
type extractedTypes = MaybeTypes<MaybeTuple>;
my only comment would be that the type of extractedTypes when you mouse over it is: type extractedTypes = {
[x: number]: string | number | boolean;
"0": string;
"1": number;
"2": boolean;
length: {};
includes: {};
toString: {};
toLocaleString: {};
push: {};
pop: {};
concat: {};
join: {};
reverse: {};
shift: {};
slice: {};
sort: {};
splice: {};
unshift: {};
indexOf: {};
lastIndexOf: {};
every: {};
some: {};
forEach: {};
map: {};
filter: {};
reduce: {};
reduceRight: {};
entries: {};
keys: {};
values: {};
find: {};
findIndex: {};
fill: {};
copyWithin: {};
}
whereas I would expect [string, number, boolean] |
Sorry, something went wrong.
|
As far as I can tell this PR does not help in the case of the rxjs pipe function: pipe<A>(op1: OperatorFunction<T, A>): Observable<A>;
pipe<A, B>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>): Observable<B>;
pipe<A, B, C>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>): Observable<C>;
pipe<A, B, C, D>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>): Observable<D>;
pipe<A, B, C, D, E>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>): Observable<E>;
here the second type param of one element in the tuple is the first type param of the next element in the tuple. Is this case currently supported? |
Sorry, something went wrong.
|
Anders Hejlsberg (@ahejlsberg) I've been playing a bit with this and the compiler does seem not recognize the fact that a mapped array type will still be an array type for the purpose of rest parameters. Ex on 3.1.0-dev.20180813 i get this error: type Promisify<T> = { [P in keyof T]: Promise<T[P]> };
// error: A rest parameter must be of an array type.
async function invokeWhenReady<T extends any[], R>(fn : (...a: T) => R, ...a: Promisify<T>) : Promise<R>{
return null as any;
}
Is this a bug, or design limitation. Will it be addressed in the future ? |
Sorry, something went wrong.
|
Titian Cernicova-Dragomir (@dragomirtitian) I think that's being tracked in #26163. |
Sorry, something went wrong.
|
Joe Calzaretta (@jcalz) I don't think that issue fixes the specific case of rest types because they use a different assignment relation. I think the problem is that the rest type checking uses isRestParameterType, which in turn uses assignable relation, not the definitely assignable relation that was fixed. |
Sorry, something went wrong.
|
Titian Cernicova-Dragomir (@dragomirtitian) Joe Calzaretta (@jcalz) Jack Williams (@jack-williams) The invokeWhenReady example above works with #26676. For example: declare let ps: Promise<string>;
declare let px: Promise<number[]>;
const r = invokeWhenReady((s, x) => ({ s, x }), ps, px) // Promise<{ s: string, x: number[] }> |
Sorry, something went wrong.
|
For anyone who is struggling with prototype properties getting mapped as well (like in Roaders's #26063 (comment)), causing A rest parameter must be of an array type error, I found out that it can be solved by explicitly keeping prototype properties out of boxing: type Boxified<T> = {
[P in keyof T]: P extends keyof [] ? T[P] : Box<T[P]>
};instead of type Boxified<T> = {
[P in keyof T]: Box<T[P]>
};(It worked implicitly in v4.4.4, but seems to require explicit condition since 4.5.2, at least in a more complicated scenario) |
Sorry, something went wrong.
|
Anders Hejlsberg (@ahejlsberg) it appears this only works on type aliases, rather than any homomorphic mapped type? type Tuple = [1, 2];
type A<T> = { [K in keyof T]: string };
type WithAlias = A<Tuple> // [string, string]
type WithoutAlias = { [K in keyof Tuple]: string };
/*
{
[x: number]: string;
0: string;
1: string;
length: string;
...
*/
It's always been surprising to me when the left and right hand sides of a type alias aren't interchangeable. At this point I wish an operator like := had been used for TS type aliases. |
Sorry, something went wrong.
|
The higher-order behavior only applies to generics, so bare object type expressions are never homomorphic; only aliases are. |
Sorry, something went wrong.
|
Sometimes I wish I could do a one-off tuple mapping inline instead of having to declare a type alias out of line. And very often, I wish I could distribute over a union type inline instead of using a distributive conditional type alias |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This PR improves our support for arrays and tuples in homomorphic mapped types (i.e. structure preserving mapped types of the form { [P in keyof T]: X }). When a homomorphic mapped type is applied to an array or tuple type, we now produce a corresponding array or tuple type where the element type(s) have been transformed.
Previously, we would treat array and tuple types like regular object types and transform all properties (including methods) of the arrays and tuples. This behavior is rarely if ever desired.
Given a homomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping operation depends on T as follows (the first two rules are existing behavior and the remaining are introduced by this PR):
Homomorphic mapped types can use ?, -?, or +? annotations to modify the optional-ness of tuple element types. For example, the predefined Partial<T> and Required<T> types have the expected effects on tuple element types:
In --strictNullChecks mode the ?, -?, or +? annotations also add or remove undefined from the element type(s) of arrays and tuples:
A readonly, -readonly, or +readonly annotation in a homomorphic mapped type currently has no effect on array or tuple elements (we might consider mapping from Array to ReadonlyArray and vice versa, although that technically isn't structure preserving because it adds or removes methods).
Homomorphic mapped type support for tuples makes it possible to transform variable length parameter lists, eliminating the need for repetitive patterns overloads in several scenarios. For example:
This PR implements much of what is suggested in #25947, but without introducing new syntax.