mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Do not include undefined in indexed access on tuple types within range of total fixed elements (#54558)
This commit is contained in:
parent
21bb2160c8
commit
4320104404
@ -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);
|
||||
|
||||
@ -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]
|
||||
|
||||
|
||||
@ -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'.
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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]
|
||||
}
|
||||
|
||||
17
tests/cases/compiler/indexedAccessWithVariableElement.ts
Normal file
17
tests/cases/compiler/indexedAccessWithVariableElement.ts
Normal 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]
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user