-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Infer fixed-size tuples from infer type parameters with extends clauses at variadic positions #51157
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
Conversation
…es at variadic positions
src/compiler/checker.ts
Outdated
if (elementFlags[startLength] & elementFlags[startLength + 1] & ElementFlags.Variadic && isTupleType(source)) { | ||
// Middle of target is [...T, ...U] and source is tuple type | ||
const impliedArity = getInferenceInfoForType(elementTypes[startLength])?.impliedArity; | ||
if (impliedArity !== undefined) { | ||
// Infer slices from source based on implied arity of T. | ||
inferFromTypes(sliceTupleType(source, startLength, endLength + sourceArity - impliedArity), elementTypes[startLength]); | ||
inferFromTypes(sliceTupleType(source, startLength + impliedArity, endLength), elementTypes[startLength + 1]); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this case was already here, it just got indented more since I put all 3 cases (1 old, 2 new ones) within the if (middleLength === 2)
block
src/compiler/checker.ts
Outdated
const trailingSlice = createTupleType(getTypeArguments(source).slice(startIndex, endIndex), source.target.elementFlags.slice(startIndex, endIndex), | ||
/*readonly*/ false, source.target.labeledElementDeclarations && source.target.labeledElementDeclarations.slice(startIndex, endIndex)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is based on the content of the sliceTupleType
. However, I wasn't able to reuse that function as the purpose of this slice is different here and sliceTupleType
wouldn't work for it - I didn't find another instance of such a "trailing slice" in the codebase so I've just inlined the thing here and adjusted for the needs here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To give more context behind this - I can't reuse sliceTupleType
as it creates array types when index > target.fixedLength
:
https://github.dev/microsoft/TypeScript/blob/acf854b636e0b8e5a12c3f9951d4edfa0fa73bcd/src/compiler/checker.ts#L15758-L15764
So it's not a literal "slice" - it's not just slicing between the startIndex
and endIndex
- it also performs a type conversion conditionally.
If not that condition, I could reuse the sliceTupleType
here as follows:
const impliedArity = constraint.target.fixedLength;
const targetEndLength = getEndElementCount(target.target, ElementFlags.Fixed);
inferFromTypes(sliceTupleType(source, sourceArity - targetEndLength - impliedArity, targetEndLength), elementTypes[startLength + 1]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks pretty good, though it'll need a sync to main
, and some extended test suites run, just in case.
# Conflicts: # src/compiler/checker.ts
@weswigham I've synced this with main. You can trigger those extended test suites now. |
@typescript-bot pack this |
Heya @weswigham, I've started to run the perf test suite on this PR at f6840d5. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the extended test suite on this PR at f6840d5. You can monitor the build here. |
Heya @weswigham, I'm starting to run the diff-based user code test suite on this PR at f6840d5. Hold tight - I'll update this comment with the log link once the build has been queued. |
Heya @weswigham, I've started to run the parallelized Definitely Typed test suite on this PR at f6840d5. You can monitor the build here. |
Heya @weswigham, I've started to run the tarball bundle task on this PR at f6840d5. You can monitor the build here. |
Heya @weswigham, I'm starting to run the diff-based top-repos suite on this PR at f6840d5. Hold tight - I'll update this comment with the log link once the build has been queued. |
Hey @weswigham, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build and an npm module you can use via |
@weswigham Here they are:
CompilerComparison Report - main..51157
System
Hosts
Scenarios
TSServerComparison Report - main..51157
System
Hosts
Scenarios
StartupComparison Report - main..51157
System
Hosts
Scenarios
Developer Information: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! I left some comments about missing things, but everything else looks great. If you can add what's missing, then I think we'll be good to merge this. 😊
src/compiler/checker.ts
Outdated
if (constraint && isTupleType(constraint) && !constraint.target.hasRestElement) { | ||
const impliedArity = constraint.target.fixedLength; | ||
inferFromTypes(sliceTupleType(source, startLength, sourceArity - (startLength + impliedArity)), elementTypes[startLength]); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we also need to infer into the rest element here, if the source arity allows (i.e. there are left over elements after inferring into the tuple constraint). Right now, we're only inferring into the variadic type, so we don't infer into the rest element in a case like this:
type SubTup2Variadic2<T extends unknown[]> = T extends [
...infer B extends [any, any],
...(infer C)[]
]
? [...B, ...[C]]
: never;
type SubTup2Variadic2Test = SubTup2Variadic2<[a: 0, b: 1, ...c: number[]]>; // Currently [0, 1, unknown], should be [0, 1, number]
src/compiler/checker.ts
Outdated
const impliedArity = constraint.target.fixedLength; | ||
const endIndex = sourceArity - getEndElementCount(target.target, ElementFlags.Fixed); | ||
const startIndex = endIndex - impliedArity; | ||
const trailingSlice = createTupleType(getTypeArguments(source).slice(startIndex, endIndex), source.target.elementFlags.slice(startIndex, endIndex), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some reason to use createTupleType
instead of sliceTupleType
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've mentioned this here but perhaps without going much into the relevant details.
Right now... I'm not exactly sure why I had to do it but I had some reason as I've used sliceTyupleType
in other branches and I've called it out in the comment that I wasn't able to use it here. I can try to refresh my memory on this one later and provide the appropriate details
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the requested clarification here: #51157 (comment)
src/compiler/checker.ts
Outdated
const trailingSlice = createTupleType(getTypeArguments(source).slice(startIndex, endIndex), source.target.elementFlags.slice(startIndex, endIndex), | ||
/*readonly*/ false, source.target.labeledElementDeclarations && source.target.labeledElementDeclarations.slice(startIndex, endIndex)); | ||
inferFromTypes(trailingSlice, elementTypes[startLength + 1]); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as in the case above, we should also infer into the rest type if possible, for cases like this:
type SubTup2TrailingVariadic2<T extends unknown[]> = T extends [
...(infer C)[],
...infer B extends [any, any],
]
? [C, ...B]
: never;
type SubTup2TrailingVariadic2Test = SubTup2TrailingVariadic2<[...a: number[], b: 1, c: 2]>; // Currently [unknown, 1, 2], should be [number, 1, 2].
@gabritto thank you for the additional test cases! I will take a look into supporting them in the coming days. |
@gabritto I pushed out the requested improvements (inferring for the rest elements appearing with the variadic elements with those fixed-size tuple constraints). Could you take another look and re-review it? |
Fixes #51138