From 99ea99b386a032187411a465e959e0904a7e336b Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 4 Apr 2022 19:53:41 -0300 Subject: [PATCH] Properly check singleton labeled tuple before unwrapping (#48554) * properly check if singleton labeled tuple has optional element * also check if labeled element is rest --- src/compiler/checker.ts | 6 ++- .../reference/singletonLabeledTuple.js | 19 ++++++++ .../reference/singletonLabeledTuple.symbols | 33 ++++++++++++++ .../reference/singletonLabeledTuple.types | 45 +++++++++++++++++++ tests/cases/compiler/singletonLabeledTuple.ts | 16 +++++++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/singletonLabeledTuple.js create mode 100644 tests/baselines/reference/singletonLabeledTuple.symbols create mode 100644 tests/baselines/reference/singletonLabeledTuple.types create mode 100644 tests/cases/compiler/singletonLabeledTuple.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b891a0ec6c0..7e144dd26f3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15860,7 +15860,11 @@ namespace ts { } function isSingletonTupleType(node: TypeNode) { - return isTupleTypeNode(node) && length(node.elements) === 1 && !isOptionalTypeNode(node.elements[0]) && !isRestTypeNode(node.elements[0]); + return isTupleTypeNode(node) && + length(node.elements) === 1 && + !isOptionalTypeNode(node.elements[0]) && + !isRestTypeNode(node.elements[0]) && + !(isNamedTupleMember(node.elements[0]) && (node.elements[0].questionToken || node.elements[0].dotDotDotToken)); } /** diff --git a/tests/baselines/reference/singletonLabeledTuple.js b/tests/baselines/reference/singletonLabeledTuple.js new file mode 100644 index 00000000000..e8299438f1e --- /dev/null +++ b/tests/baselines/reference/singletonLabeledTuple.js @@ -0,0 +1,19 @@ +//// [singletonLabeledTuple.ts] +type AliasOptional = [p?: number] + +// literal type vs type alias +type Literal = [p?: number] extends [unknown] ? true : false // Expect `Literal` to be `false` +type Alias = AliasOptional extends [unknown] ? true : false // Expect `Alias` to be `false` + +// labeled tuple vs normal tuple +type Labeled = [p?: number] extends [unknown] ? true : false // Expect `Labeled` to be `false` +type Normal = [number?] extends [unknown] ? true : false // Expect `Normal` to be `false` + + +type AliasRest = [...p: number[]]; + +type LiteralRest = [...p: number[]] extends [unknown] ? true : false; // Expect `LiteralRest` to be `false` +type AliasedRest = AliasRest extends [unknown] ? true : false; // Expect `AliasedRest` to be `false` +type NormalRest = [...number[]] extends [unknown] ? true : false; // Expect `NormalRest` to be `false` + +//// [singletonLabeledTuple.js] diff --git a/tests/baselines/reference/singletonLabeledTuple.symbols b/tests/baselines/reference/singletonLabeledTuple.symbols new file mode 100644 index 00000000000..327ab83ac16 --- /dev/null +++ b/tests/baselines/reference/singletonLabeledTuple.symbols @@ -0,0 +1,33 @@ +=== tests/cases/compiler/singletonLabeledTuple.ts === +type AliasOptional = [p?: number] +>AliasOptional : Symbol(AliasOptional, Decl(singletonLabeledTuple.ts, 0, 0)) + +// literal type vs type alias +type Literal = [p?: number] extends [unknown] ? true : false // Expect `Literal` to be `false` +>Literal : Symbol(Literal, Decl(singletonLabeledTuple.ts, 0, 33)) + +type Alias = AliasOptional extends [unknown] ? true : false // Expect `Alias` to be `false` +>Alias : Symbol(Alias, Decl(singletonLabeledTuple.ts, 3, 60)) +>AliasOptional : Symbol(AliasOptional, Decl(singletonLabeledTuple.ts, 0, 0)) + +// labeled tuple vs normal tuple +type Labeled = [p?: number] extends [unknown] ? true : false // Expect `Labeled` to be `false` +>Labeled : Symbol(Labeled, Decl(singletonLabeledTuple.ts, 4, 59)) + +type Normal = [number?] extends [unknown] ? true : false // Expect `Normal` to be `false` +>Normal : Symbol(Normal, Decl(singletonLabeledTuple.ts, 7, 60)) + + +type AliasRest = [...p: number[]]; +>AliasRest : Symbol(AliasRest, Decl(singletonLabeledTuple.ts, 8, 56)) + +type LiteralRest = [...p: number[]] extends [unknown] ? true : false; // Expect `LiteralRest` to be `false` +>LiteralRest : Symbol(LiteralRest, Decl(singletonLabeledTuple.ts, 11, 34)) + +type AliasedRest = AliasRest extends [unknown] ? true : false; // Expect `AliasedRest` to be `false` +>AliasedRest : Symbol(AliasedRest, Decl(singletonLabeledTuple.ts, 13, 69)) +>AliasRest : Symbol(AliasRest, Decl(singletonLabeledTuple.ts, 8, 56)) + +type NormalRest = [...number[]] extends [unknown] ? true : false; // Expect `NormalRest` to be `false` +>NormalRest : Symbol(NormalRest, Decl(singletonLabeledTuple.ts, 14, 62)) + diff --git a/tests/baselines/reference/singletonLabeledTuple.types b/tests/baselines/reference/singletonLabeledTuple.types new file mode 100644 index 00000000000..44ca6b978d1 --- /dev/null +++ b/tests/baselines/reference/singletonLabeledTuple.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/singletonLabeledTuple.ts === +type AliasOptional = [p?: number] +>AliasOptional : AliasOptional + +// literal type vs type alias +type Literal = [p?: number] extends [unknown] ? true : false // Expect `Literal` to be `false` +>Literal : false +>true : true +>false : false + +type Alias = AliasOptional extends [unknown] ? true : false // Expect `Alias` to be `false` +>Alias : false +>true : true +>false : false + +// labeled tuple vs normal tuple +type Labeled = [p?: number] extends [unknown] ? true : false // Expect `Labeled` to be `false` +>Labeled : false +>true : true +>false : false + +type Normal = [number?] extends [unknown] ? true : false // Expect `Normal` to be `false` +>Normal : false +>true : true +>false : false + + +type AliasRest = [...p: number[]]; +>AliasRest : AliasRest + +type LiteralRest = [...p: number[]] extends [unknown] ? true : false; // Expect `LiteralRest` to be `false` +>LiteralRest : false +>true : true +>false : false + +type AliasedRest = AliasRest extends [unknown] ? true : false; // Expect `AliasedRest` to be `false` +>AliasedRest : false +>true : true +>false : false + +type NormalRest = [...number[]] extends [unknown] ? true : false; // Expect `NormalRest` to be `false` +>NormalRest : false +>true : true +>false : false + diff --git a/tests/cases/compiler/singletonLabeledTuple.ts b/tests/cases/compiler/singletonLabeledTuple.ts new file mode 100644 index 00000000000..27d457ce35e --- /dev/null +++ b/tests/cases/compiler/singletonLabeledTuple.ts @@ -0,0 +1,16 @@ +type AliasOptional = [p?: number] + +// literal type vs type alias +type Literal = [p?: number] extends [unknown] ? true : false // Expect `Literal` to be `false` +type Alias = AliasOptional extends [unknown] ? true : false // Expect `Alias` to be `false` + +// labeled tuple vs normal tuple +type Labeled = [p?: number] extends [unknown] ? true : false // Expect `Labeled` to be `false` +type Normal = [number?] extends [unknown] ? true : false // Expect `Normal` to be `false` + + +type AliasRest = [...p: number[]]; + +type LiteralRest = [...p: number[]] extends [unknown] ? true : false; // Expect `LiteralRest` to be `false` +type AliasedRest = AliasRest extends [unknown] ? true : false; // Expect `AliasedRest` to be `false` +type NormalRest = [...number[]] extends [unknown] ? true : false; // Expect `NormalRest` to be `false` \ No newline at end of file