Do not include undefined in indexed access on tuple types within range of total fixed elements (#54558)

This commit is contained in:
Mateusz Burzyński 2023-07-29 00:59:31 +02:00 committed by GitHub
parent 21bb2160c8
commit 4320104404
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 156 additions and 34 deletions

View File

@ -16277,6 +16277,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return type.elementFlags.length - findLastIndex(type.elementFlags, f => !(f & flags)) - 1;
}
function getTotalFixedElementCount(type: TupleType) {
return type.fixedLength + getEndElementCount(type, ElementFlags.Fixed);
}
function getElementTypes(type: TupleTypeReference): readonly Type[] {
const typeArguments = getTypeArguments(type);
const arity = getTypeReferenceArity(type);
@ -17402,10 +17406,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
if (index >= 0) {
errorIfWritingToReadonlyIndex(getIndexInfoOfType(objectType, numberType));
return mapType(objectType, t => {
const restType = getRestTypeOfTupleType(t as TupleTypeReference) || undefinedType;
return accessFlags & AccessFlags.IncludeUndefined ? getUnionType([restType, missingType]) : restType;
});
return getTupleElementTypeOutOfStartCount(objectType, index, accessFlags & AccessFlags.IncludeUndefined ? missingType : undefined);
}
}
}
@ -17745,8 +17746,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved
// eagerly using the constraint type of 'this' at the given location.
if (isGenericIndexType(indexType) || (accessNode && accessNode.kind !== SyntaxKind.IndexedAccessType ?
isGenericTupleType(objectType) && !indexTypeLessThan(indexType, objectType.target.fixedLength) :
isGenericObjectType(objectType) && !(isTupleType(objectType) && indexTypeLessThan(indexType, objectType.target.fixedLength)) || isGenericReducibleType(objectType))) {
isGenericTupleType(objectType) && !indexTypeLessThan(indexType, getTotalFixedElementCount(objectType.target)) :
isGenericObjectType(objectType) && !(isTupleType(objectType) && indexTypeLessThan(indexType, getTotalFixedElementCount(objectType.target))) || isGenericReducibleType(objectType))) {
if (objectType.flags & TypeFlags.AnyOrUnknown) {
return objectType;
}
@ -23325,18 +23326,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return propType;
}
if (everyType(type, isTupleType)) {
return mapType(type, t => {
const tupleType = t as TupleTypeReference;
const restType = getRestTypeOfTupleType(tupleType);
if (!restType) {
return undefinedType;
}
if (compilerOptions.noUncheckedIndexedAccess &&
index >= tupleType.target.fixedLength + getEndElementCount(tupleType.target, ElementFlags.Fixed)) {
return getUnionType([restType, undefinedType]);
}
return restType;
});
return getTupleElementTypeOutOfStartCount(type, index, compilerOptions.noUncheckedIndexedAccess ? undefinedType : undefined);
}
return undefined;
}
@ -23454,6 +23444,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return getElementTypeOfSliceOfTupleType(type, type.target.fixedLength);
}
function getTupleElementTypeOutOfStartCount(type: Type, index: number, undefinedOrMissingType: Type | undefined) {
return mapType(type, t => {
const tupleType = t as TupleTypeReference;
const restType = getRestTypeOfTupleType(tupleType);
if (!restType) {
return undefinedType;
}
if (undefinedOrMissingType && index >= getTotalFixedElementCount(tupleType.target)) {
return getUnionType([restType, undefinedOrMissingType]);
}
return restType;
});
}
function getRestArrayTypeOfTupleType(type: TupleTypeReference) {
const restType = getRestTypeOfTupleType(type);
return restType && createArrayType(restType);

View File

@ -41,7 +41,7 @@ declare const strings2: [string, ...Array<string>, string]
const [s3, s4, s5] = strings2;
>s3 : string
>s4 : string | undefined
>s4 : string
>s5 : string | undefined
>strings2 : [string, ...string[], string]

View File

@ -0,0 +1,27 @@
indexedAccessWithVariableElement.ts(7,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.
indexedAccessWithVariableElement.ts(13,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.
==== indexedAccessWithVariableElement.ts (2 errors) ====
// repro from https://github.com/microsoft/TypeScript/issues/54420
declare const array1: [...number[], number]
const el1: number = array1[0]
declare const array2: [...number[], number]
const el2: number = array2[1]
~~~
!!! error TS2322: Type 'number | undefined' is not assignable to type 'number'.
!!! error TS2322: Type 'undefined' is not assignable to type 'number'.
declare const array3: [number, ...number[], number]
const el3: number = array3[1]
declare const array4: [number, ...number[], number]
const el4: number = array4[2]
~~~
!!! error TS2322: Type 'number | undefined' is not assignable to type 'number'.
!!! error TS2322: Type 'undefined' is not assignable to type 'number'.

View File

@ -0,0 +1,33 @@
//// [tests/cases/compiler/indexedAccessWithVariableElement.ts] ////
=== indexedAccessWithVariableElement.ts ===
// repro from https://github.com/microsoft/TypeScript/issues/54420
declare const array1: [...number[], number]
>array1 : Symbol(array1, Decl(indexedAccessWithVariableElement.ts, 2, 13))
const el1: number = array1[0]
>el1 : Symbol(el1, Decl(indexedAccessWithVariableElement.ts, 3, 5))
>array1 : Symbol(array1, Decl(indexedAccessWithVariableElement.ts, 2, 13))
declare const array2: [...number[], number]
>array2 : Symbol(array2, Decl(indexedAccessWithVariableElement.ts, 5, 13))
const el2: number = array2[1]
>el2 : Symbol(el2, Decl(indexedAccessWithVariableElement.ts, 6, 5))
>array2 : Symbol(array2, Decl(indexedAccessWithVariableElement.ts, 5, 13))
declare const array3: [number, ...number[], number]
>array3 : Symbol(array3, Decl(indexedAccessWithVariableElement.ts, 8, 13))
const el3: number = array3[1]
>el3 : Symbol(el3, Decl(indexedAccessWithVariableElement.ts, 9, 5))
>array3 : Symbol(array3, Decl(indexedAccessWithVariableElement.ts, 8, 13))
declare const array4: [number, ...number[], number]
>array4 : Symbol(array4, Decl(indexedAccessWithVariableElement.ts, 11, 13))
const el4: number = array4[2]
>el4 : Symbol(el4, Decl(indexedAccessWithVariableElement.ts, 12, 5))
>array4 : Symbol(array4, Decl(indexedAccessWithVariableElement.ts, 11, 13))

View File

@ -0,0 +1,41 @@
//// [tests/cases/compiler/indexedAccessWithVariableElement.ts] ////
=== indexedAccessWithVariableElement.ts ===
// repro from https://github.com/microsoft/TypeScript/issues/54420
declare const array1: [...number[], number]
>array1 : [...number[], number]
const el1: number = array1[0]
>el1 : number
>array1[0] : number
>array1 : [...number[], number]
>0 : 0
declare const array2: [...number[], number]
>array2 : [...number[], number]
const el2: number = array2[1]
>el2 : number
>array2[1] : number | undefined
>array2 : [...number[], number]
>1 : 1
declare const array3: [number, ...number[], number]
>array3 : [number, ...number[], number]
const el3: number = array3[1]
>el3 : number
>array3[1] : number
>array3 : [number, ...number[], number]
>1 : 1
declare const array4: [number, ...number[], number]
>array4 : [number, ...number[], number]
const el4: number = array4[2]
>el4 : number
>array4[2] : number | undefined
>array4 : [number, ...number[], number]
>2 : 2

View File

@ -144,7 +144,7 @@ variadicTuples1.ts(411,7): error TS2322: Type '[boolean, false]' is not assignab
function f1<T extends unknown[]>(t: [string, ...T, number], n: number) {
const a = t[0]; // string
const b = t[1]; // [string, ...T, number][1]
const b = t[1]; // number | T[number]
const c = t[2]; // [string, ...T, number][2]
const d = t[n]; // [string, ...T, number][number]
}
@ -160,7 +160,7 @@ variadicTuples1.ts(411,7): error TS2322: Type '[boolean, false]' is not assignab
function f3<T extends unknown[]>(t: [string, ...T, number]) {
let [...ax] = t; // [string, ...T, number]
let [b1, ...bx] = t; // string, [...T, number]
let [c1, c2, ...cx] = t; // string, [string, ...T, number][1], (number | T[number])[]
let [c1, c2, ...cx] = t; // string, number | T[number], (number | T[number])[]
}
// Mapped types applied to variadic tuple types

View File

@ -88,7 +88,7 @@ function f0<T extends unknown[]>(t: [string, ...T], n: number) {
function f1<T extends unknown[]>(t: [string, ...T, number], n: number) {
const a = t[0]; // string
const b = t[1]; // [string, ...T, number][1]
const b = t[1]; // number | T[number]
const c = t[2]; // [string, ...T, number][2]
const d = t[n]; // [string, ...T, number][number]
}
@ -104,7 +104,7 @@ function f2<T extends unknown[]>(t: [string, ...T]) {
function f3<T extends unknown[]>(t: [string, ...T, number]) {
let [...ax] = t; // [string, ...T, number]
let [b1, ...bx] = t; // string, [...T, number]
let [c1, c2, ...cx] = t; // string, [string, ...T, number][1], (number | T[number])[]
let [c1, c2, ...cx] = t; // string, number | T[number], (number | T[number])[]
}
// Mapped types applied to variadic tuple types
@ -478,7 +478,7 @@ function f0(t, n) {
}
function f1(t, n) {
var a = t[0]; // string
var b = t[1]; // [string, ...T, number][1]
var b = t[1]; // number | T[number]
var c = t[2]; // [string, ...T, number][2]
var d = t[n]; // [string, ...T, number][number]
}
@ -491,7 +491,7 @@ function f2(t) {
function f3(t) {
var ax = t.slice(0); // [string, ...T, number]
var b1 = t[0], bx = t.slice(1); // string, [...T, number]
var c1 = t[0], c2 = t[1], cx = t.slice(2); // string, [string, ...T, number][1], (number | T[number])[]
var c1 = t[0], c2 = t[1], cx = t.slice(2); // string, number | T[number], (number | T[number])[]
}
var tm1 = fm1([['abc'], [42], [true], ['def']]); // [boolean, string]
function gx1(u, v) {

View File

@ -285,7 +285,7 @@ function f1<T extends unknown[]>(t: [string, ...T, number], n: number) {
>t : Symbol(t, Decl(variadicTuples1.ts, 85, 33))
>0 : Symbol(0)
const b = t[1]; // [string, ...T, number][1]
const b = t[1]; // number | T[number]
>b : Symbol(b, Decl(variadicTuples1.ts, 87, 9))
>t : Symbol(t, Decl(variadicTuples1.ts, 85, 33))
@ -338,7 +338,7 @@ function f3<T extends unknown[]>(t: [string, ...T, number]) {
>bx : Symbol(bx, Decl(variadicTuples1.ts, 102, 12))
>t : Symbol(t, Decl(variadicTuples1.ts, 100, 33))
let [c1, c2, ...cx] = t; // string, [string, ...T, number][1], (number | T[number])[]
let [c1, c2, ...cx] = t; // string, number | T[number], (number | T[number])[]
>c1 : Symbol(c1, Decl(variadicTuples1.ts, 103, 9))
>c2 : Symbol(c2, Decl(variadicTuples1.ts, 103, 12))
>cx : Symbol(cx, Decl(variadicTuples1.ts, 103, 16))

View File

@ -344,9 +344,9 @@ function f1<T extends unknown[]>(t: [string, ...T, number], n: number) {
>t : [string, ...T, number]
>0 : 0
const b = t[1]; // [string, ...T, number][1]
>b : [string, ...T, number][1]
>t[1] : [string, ...T, number][1]
const b = t[1]; // number | T[number]
>b : number | T[number]
>t[1] : number | T[number]
>t : [string, ...T, number]
>1 : 1
@ -398,9 +398,9 @@ function f3<T extends unknown[]>(t: [string, ...T, number]) {
>bx : [...T, number]
>t : [string, ...T, number]
let [c1, c2, ...cx] = t; // string, [string, ...T, number][1], (number | T[number])[]
let [c1, c2, ...cx] = t; // string, number | T[number], (number | T[number])[]
>c1 : string
>c2 : [string, ...T, number][1]
>c2 : number | T[number]
>cx : (number | T[number])[]
>t : [string, ...T, number]
}

View File

@ -0,0 +1,17 @@
// @strict: true
// @noUncheckedIndexedAccess: true
// @noEmit: true
// repro from https://github.com/microsoft/TypeScript/issues/54420
declare const array1: [...number[], number]
const el1: number = array1[0]
declare const array2: [...number[], number]
const el2: number = array2[1]
declare const array3: [number, ...number[], number]
const el3: number = array3[1]
declare const array4: [number, ...number[], number]
const el4: number = array4[2]

View File

@ -88,7 +88,7 @@ function f0<T extends unknown[]>(t: [string, ...T], n: number) {
function f1<T extends unknown[]>(t: [string, ...T, number], n: number) {
const a = t[0]; // string
const b = t[1]; // [string, ...T, number][1]
const b = t[1]; // number | T[number]
const c = t[2]; // [string, ...T, number][2]
const d = t[n]; // [string, ...T, number][number]
}
@ -104,7 +104,7 @@ function f2<T extends unknown[]>(t: [string, ...T]) {
function f3<T extends unknown[]>(t: [string, ...T, number]) {
let [...ax] = t; // [string, ...T, number]
let [b1, ...bx] = t; // string, [...T, number]
let [c1, c2, ...cx] = t; // string, [string, ...T, number][1], (number | T[number])[]
let [c1, c2, ...cx] = t; // string, number | T[number], (number | T[number])[]
}
// Mapped types applied to variadic tuple types