Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22013,7 +22013,10 @@ namespace ts {
return reduceLeft((<UnionType>type).types, (facts, t) => facts | getTypeFacts(t), TypeFacts.None);
}
if (flags & TypeFlags.Intersection) {
return reduceLeft((<UnionType>type).types, (facts, t) => facts & getTypeFacts(t), TypeFacts.All);
// When an intersection contains a primitive type we ignore object type constituents as they are
// presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type.
const containsPrimitive = maybeTypeOfKind(type, TypeFlags.Primitive);
return reduceLeft((<UnionType>type).types, (facts, t) => containsPrimitive && getBaseConstraintOrType(t).flags & TypeFlags.Object ? facts : facts & getTypeFacts(t), TypeFacts.All);
}
return TypeFacts.All;
}
Expand Down
42 changes: 42 additions & 0 deletions tests/baselines/reference/taggedPrimitiveNarrowing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//// [taggedPrimitiveNarrowing.ts]
type Hash = string & { __hash: true };

function getHashLength(hash: Hash): number {
if (typeof hash !== "string") {
throw new Error("This doesn't look like a hash");
}
return hash.length;
}

function getHashLength2<T extends { __tag__: unknown}>(hash: string & T): number {
if (typeof hash !== "string") {
throw new Error("This doesn't look like a hash");
}
return hash.length;
}


//// [taggedPrimitiveNarrowing.js]
"use strict";
function getHashLength(hash) {
if (typeof hash !== "string") {
throw new Error("This doesn't look like a hash");
}
return hash.length;
}
function getHashLength2(hash) {
if (typeof hash !== "string") {
throw new Error("This doesn't look like a hash");
}
return hash.length;
}


//// [taggedPrimitiveNarrowing.d.ts]
declare type Hash = string & {
__hash: true;
};
declare function getHashLength(hash: Hash): number;
declare function getHashLength2<T extends {
__tag__: unknown;
}>(hash: string & T): number;
41 changes: 41 additions & 0 deletions tests/baselines/reference/taggedPrimitiveNarrowing.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
=== tests/cases/compiler/taggedPrimitiveNarrowing.ts ===
type Hash = string & { __hash: true };
>Hash : Symbol(Hash, Decl(taggedPrimitiveNarrowing.ts, 0, 0))
>__hash : Symbol(__hash, Decl(taggedPrimitiveNarrowing.ts, 0, 22))

function getHashLength(hash: Hash): number {
>getHashLength : Symbol(getHashLength, Decl(taggedPrimitiveNarrowing.ts, 0, 38))
>hash : Symbol(hash, Decl(taggedPrimitiveNarrowing.ts, 2, 23))
>Hash : Symbol(Hash, Decl(taggedPrimitiveNarrowing.ts, 0, 0))

if (typeof hash !== "string") {
>hash : Symbol(hash, Decl(taggedPrimitiveNarrowing.ts, 2, 23))

throw new Error("This doesn't look like a hash");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
return hash.length;
>hash.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>hash : Symbol(hash, Decl(taggedPrimitiveNarrowing.ts, 2, 23))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
}

function getHashLength2<T extends { __tag__: unknown}>(hash: string & T): number {
>getHashLength2 : Symbol(getHashLength2, Decl(taggedPrimitiveNarrowing.ts, 7, 1))
>T : Symbol(T, Decl(taggedPrimitiveNarrowing.ts, 9, 24))
>__tag__ : Symbol(__tag__, Decl(taggedPrimitiveNarrowing.ts, 9, 35))
>hash : Symbol(hash, Decl(taggedPrimitiveNarrowing.ts, 9, 55))
>T : Symbol(T, Decl(taggedPrimitiveNarrowing.ts, 9, 24))

if (typeof hash !== "string") {
>hash : Symbol(hash, Decl(taggedPrimitiveNarrowing.ts, 9, 55))

throw new Error("This doesn't look like a hash");
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
return hash.length;
>hash.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>hash : Symbol(hash, Decl(taggedPrimitiveNarrowing.ts, 9, 55))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
}

49 changes: 49 additions & 0 deletions tests/baselines/reference/taggedPrimitiveNarrowing.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
=== tests/cases/compiler/taggedPrimitiveNarrowing.ts ===
type Hash = string & { __hash: true };
>Hash : Hash
>__hash : true
>true : true

function getHashLength(hash: Hash): number {
>getHashLength : (hash: Hash) => number
>hash : Hash

if (typeof hash !== "string") {
>typeof hash !== "string" : boolean
>typeof hash : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>hash : Hash
>"string" : "string"

throw new Error("This doesn't look like a hash");
>new Error("This doesn't look like a hash") : Error
>Error : ErrorConstructor
>"This doesn't look like a hash" : "This doesn't look like a hash"
}
return hash.length;
>hash.length : number
>hash : Hash
>length : number
}

function getHashLength2<T extends { __tag__: unknown}>(hash: string & T): number {
>getHashLength2 : <T extends { __tag__: unknown; }>(hash: string & T) => number
>__tag__ : unknown
>hash : string & T

if (typeof hash !== "string") {
>typeof hash !== "string" : boolean
>typeof hash : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>hash : string & T
>"string" : "string"

throw new Error("This doesn't look like a hash");
>new Error("This doesn't look like a hash") : Error
>Error : ErrorConstructor
>"This doesn't look like a hash" : "This doesn't look like a hash"
}
return hash.length;
>hash.length : number
>hash : string & T
>length : number
}

18 changes: 18 additions & 0 deletions tests/cases/compiler/taggedPrimitiveNarrowing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// @strict: true
// @declaration: true

type Hash = string & { __hash: true };

function getHashLength(hash: Hash): number {
if (typeof hash !== "string") {
throw new Error("This doesn't look like a hash");
}
return hash.length;
}

function getHashLength2<T extends { __tag__: unknown}>(hash: string & T): number {
if (typeof hash !== "string") {
throw new Error("This doesn't look like a hash");
}
return hash.length;
}