Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
48 changes: 27 additions & 21 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17051,32 +17051,24 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
return type;

function addSpans(texts: readonly string[] | string, types: readonly Type[]): boolean {
const isTextsArray = isArray(texts);
function addSpans(texts: readonly string[], types: readonly Type[]): boolean {
for (let i = 0; i < types.length; i++) {
const t = types[i];
const addText = isTextsArray ? texts[i + 1] : texts;
if (t.flags & (TypeFlags.Literal | TypeFlags.Null | TypeFlags.Undefined)) {
text += getTemplateStringForType(t) || "";
text += addText;
if (!isTextsArray) return true;
text += texts[i + 1];
}
else if (t.flags & TypeFlags.TemplateLiteral) {
text += (t as TemplateLiteralType).texts[0];
if (!addSpans((t as TemplateLiteralType).texts, (t as TemplateLiteralType).types)) return false;
text += addText;
if (!isTextsArray) return true;
text += texts[i + 1];
}
else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t)) {
newTypes.push(t);
newTexts.push(text);
text = addText;
text = texts[i + 1];
}
else if (t.flags & TypeFlags.Intersection) {
const added = addSpans(texts[i + 1], (t as IntersectionType).types);
if (!added) return false;
}
else if (isTextsArray) {
else {
return false;
}
}
Expand All @@ -17094,13 +17086,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

function createTemplateLiteralType(texts: readonly string[], types: readonly Type[]) {
const type = createType(TypeFlags.TemplateLiteral) as TemplateLiteralType;
type.objectFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
type.texts = texts;
type.types = types;
return type;
}

function getStringMappingType(symbol: Symbol, type: Type): Type {
return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) :
type.flags & TypeFlags.Intersection ? getIntersectionType(map((type as IntersectionType).types, t => getStringMappingType(symbol, t))) :
type.flags & TypeFlags.StringLiteral ? getStringLiteralType(applyStringMapping(symbol, (type as StringLiteralType).value)) :
type.flags & TypeFlags.TemplateLiteral ? getTemplateLiteralType(...applyTemplateStringMapping(symbol, (type as TemplateLiteralType).texts, (type as TemplateLiteralType).types)) :
// Mapping<Mapping<T>> === Mapping<T>
Expand Down Expand Up @@ -17404,7 +17398,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function isPatternLiteralPlaceholderType(type: Type): boolean {
return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || isPatternLiteralType(type);
if (type.flags & TypeFlags.Intersection) {
return some((type as IntersectionType).types, isPatternLiteralPlaceholderType);
}

if (type.flags & (TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) {
return isPatternLiteralType(type);
}

if (type.flags & TypeFlags.ESSymbolLike) {
return false;
}

return !!(type.flags & (TypeFlags.Any | TypeFlags.Primitive));
}

function isPatternLiteralType(type: Type) {
Expand All @@ -17425,12 +17431,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function getGenericObjectFlags(type: Type): ObjectFlags {
if (type.flags & TypeFlags.UnionOrIntersection) {
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
reduceLeft((type as UnionOrIntersectionType).types, (flags, t) => flags | getGenericObjectFlags(t), 0);
if (type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral)) {
if (!((type as UnionOrIntersectionType | TemplateLiteralType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
(type as UnionOrIntersectionType | TemplateLiteralType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
reduceLeft((type as UnionOrIntersectionType | TemplateLiteralType).types, (flags, t) => flags | getGenericObjectFlags(t), 0);
}
return (type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericType;
return (type as UnionOrIntersectionType | TemplateLiteralType).objectFlags & ObjectFlags.IsGenericType;
}
if (type.flags & TypeFlags.Substitution) {
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
Expand All @@ -17440,7 +17446,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType;
}
return (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(type) || isGenericTupleType(type) ? ObjectFlags.IsGenericObjectType : 0) |
(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0);
(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0);
}

function getSimplifiedType(type: Type, writing: boolean): Type {
Expand Down Expand Up @@ -23856,7 +23862,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
objectFlags & ObjectFlags.Reference && ((type as TypeReference).node || forEach(getTypeArguments(type as TypeReference), couldContainTypeVariables)) ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ||
objectFlags & (ObjectFlags.Mapped | ObjectFlags.ReverseMapped | ObjectFlags.ObjectRestType | ObjectFlags.InstantiationExpressionType)) ||
type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && !isNonGenericTopLevelType(type) && some((type as UnionOrIntersectionType).types, couldContainTypeVariables));
type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) && !(type.flags & TypeFlags.EnumLiteral) && !isNonGenericTopLevelType(type) && some((type as UnionOrIntersectionType | TemplateLiteralType).types, couldContainTypeVariables));
if (type.flags & TypeFlags.ObjectFlagsType) {
(type as ObjectFlagsType).objectFlags |= ObjectFlags.CouldContainTypeVariablesComputed | (result ? ObjectFlags.CouldContainTypeVariables : 0);
}
Expand Down
8 changes: 5 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6107,7 +6107,7 @@ export const enum TypeFlags {
Instantiable = InstantiableNonPrimitive | InstantiablePrimitive,
StructuredOrInstantiable = StructuredType | Instantiable,
/** @internal */
ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection,
ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection | TemplateLiteral,
/** @internal */
Simplifiable = IndexedAccess | Conditional,
/** @internal */
Expand Down Expand Up @@ -6263,7 +6263,7 @@ export const enum ObjectFlags {
/** @internal */
IdenticalBaseTypeExists = 1 << 26, // has a defined cachedEquivalentBaseType member

// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
// Flags that require TypeFlags.UnionOrIntersection, TypeFlags.Substitution, or TypeFlags.TemplateLiteral
/** @internal */
IsGenericTypeComputed = 1 << 21, // IsGenericObjectType flag has been computed
/** @internal */
Expand All @@ -6290,7 +6290,7 @@ export const enum ObjectFlags {
}

/** @internal */
export type ObjectFlagsType = NullableType | ObjectType | UnionType | IntersectionType;
export type ObjectFlagsType = NullableType | ObjectType | UnionType | IntersectionType | TemplateLiteralType;

// Object types (TypeFlags.ObjectType)
export interface ObjectType extends Type {
Expand Down Expand Up @@ -6628,6 +6628,8 @@ export interface ConditionalType extends InstantiableType {
}

export interface TemplateLiteralType extends InstantiableType {
/** @internal */
objectFlags: ObjectFlags;
texts: readonly string[]; // Always one element longer than types
types: readonly Type[]; // Always at least one element
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts(20,11): error TS2590: Expression produces a union type that is too complex to represent.


==== tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts (1 errors) ====
type R = `${number}a` & {
_thing: true;
};

type _S = "1" | "2" | "3" | "4" | "5" | "6";

type S = `${_S}${_S}${_S}`;


type T = R | S;
type X = `${T} ${T}`;

export type Props = Partial<{
x: X;
}>;

const a1: Props = {};
const a2: Props = {};

const b = { ...a1, ...a2 };
~~~~~~~~~~~~~~~~
!!! error TS2590: Expression produces a union type that is too complex to represent.

export { b };

8 changes: 4 additions & 4 deletions tests/baselines/reference/intrinsicTypes.types
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type TU5 = Uppercase<never>; // never
>TU5 : never

type TU6 = Uppercase<42>; // Error
>TU6 : 42
>TU6 : Uppercase<"42">

type TL1 = Lowercase<'HELLO'>; // "hello"
>TL1 : "hello"
Expand All @@ -33,7 +33,7 @@ type TL5 = Lowercase<never>; // never
>TL5 : never

type TL6 = Lowercase<42>; // Error
>TL6 : 42
>TL6 : Lowercase<"42">

type TC1 = Capitalize<'hello'>; // "Hello"
>TC1 : "Hello"
Expand All @@ -51,7 +51,7 @@ type TC5 = Capitalize<never>; // never
>TC5 : never

type TC6 = Capitalize<42>; // Error
>TC6 : 42
>TC6 : Capitalize<"42">

type TN1 = Uncapitalize<'Hello'>; // "hello"
>TN1 : "hello"
Expand All @@ -69,7 +69,7 @@ type TN5 = Uncapitalize<never>; // never
>TN5 : never

type TN6 = Uncapitalize<42>; // Error
>TN6 : 42
>TN6 : Uncapitalize<"42">

type TX1<S extends string> = Uppercase<`aB${S}`>;
>TX1 : `AB${Uppercase<S>}`
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/templateLiteralIntersection.types
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type OriginA1 = `${A}`
>OriginA1 : "a"

type OriginA2 = `${MixA}`
>OriginA2 : "a"
>OriginA2 : `${MixA}`

type B = `${typeof a}`
>B : "a"
Expand All @@ -30,14 +30,14 @@ type OriginB1 = `${B}`
>OriginB1 : "a"

type OriginB2 = `${MixB}`
>OriginB2 : "a"
>OriginB2 : `${MixB}`

type MixC = { foo: string } & A
>MixC : { foo: string; } & "a"
>foo : string

type OriginC = `${MixC}`
>OriginC : "a"
>OriginC : `${MixC}`

type MixD<T extends string> =
>MixD : `${T & { foo: string; }}`
Expand All @@ -46,7 +46,7 @@ type MixD<T extends string> =
>foo : string

type OriginD = `${MixD<A & { foo: string }> & { foo: string }}`;
>OriginD : "a"
>OriginD : `${`${"a" & { foo: string; } & { foo: string; }}` & { foo: string; }}`
>foo : string
>foo : string

Expand Down
41 changes: 41 additions & 0 deletions tests/baselines/reference/templateLiteralIntersection2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
tests/cases/compiler/templateLiteralIntersection2.ts(7,12): error TS2345: Argument of type '"foo/bar"' is not assignable to parameter of type '`${Path}/${Path}`'.
tests/cases/compiler/templateLiteralIntersection2.ts(20,10): error TS2345: Argument of type '""' is not assignable to parameter of type '`a${string}` & `${string}a`'.
Type '""' is not assignable to type '`a${string}`'.
tests/cases/compiler/templateLiteralIntersection2.ts(22,10): error TS2345: Argument of type '"ab"' is not assignable to parameter of type '`a${string}` & `${string}a`'.
Type '"ab"' is not assignable to type '`${string}a`'.


==== tests/cases/compiler/templateLiteralIntersection2.ts (3 errors) ====
type Path = string & { _pathBrand: any };

type JoinedPath = `${Path}/${Path}`;

declare function joinedPath(p: JoinedPath): void;

joinedPath("foo/bar");
~~~~~~~~~
!!! error TS2345: Argument of type '"foo/bar"' is not assignable to parameter of type '`${Path}/${Path}`'.

declare const somePath: Path;

joinedPath(`${somePath}/${somePath}`);


type StartsWithA = `a${string}`;
type EndsWithA = `${string}a`;


declare function withinAs(p: StartsWithA & EndsWithA): void;

withinAs("");
~~
!!! error TS2345: Argument of type '""' is not assignable to parameter of type '`a${string}` & `${string}a`'.
!!! error TS2345: Type '""' is not assignable to type '`a${string}`'.
withinAs("a");
withinAs("ab");
~~~~
!!! error TS2345: Argument of type '"ab"' is not assignable to parameter of type '`a${string}` & `${string}a`'.
!!! error TS2345: Type '"ab"' is not assignable to type '`${string}a`'.
withinAs("aba");
withinAs("abavvvva");

56 changes: 56 additions & 0 deletions tests/baselines/reference/templateLiteralIntersection2.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
=== tests/cases/compiler/templateLiteralIntersection2.ts ===
type Path = string & { _pathBrand: any };
>Path : Symbol(Path, Decl(templateLiteralIntersection2.ts, 0, 0))
>_pathBrand : Symbol(_pathBrand, Decl(templateLiteralIntersection2.ts, 0, 22))

type JoinedPath = `${Path}/${Path}`;
>JoinedPath : Symbol(JoinedPath, Decl(templateLiteralIntersection2.ts, 0, 41))
>Path : Symbol(Path, Decl(templateLiteralIntersection2.ts, 0, 0))
>Path : Symbol(Path, Decl(templateLiteralIntersection2.ts, 0, 0))

declare function joinedPath(p: JoinedPath): void;
>joinedPath : Symbol(joinedPath, Decl(templateLiteralIntersection2.ts, 2, 36))
>p : Symbol(p, Decl(templateLiteralIntersection2.ts, 4, 28))
>JoinedPath : Symbol(JoinedPath, Decl(templateLiteralIntersection2.ts, 0, 41))

joinedPath("foo/bar");
>joinedPath : Symbol(joinedPath, Decl(templateLiteralIntersection2.ts, 2, 36))

declare const somePath: Path;
>somePath : Symbol(somePath, Decl(templateLiteralIntersection2.ts, 8, 13))
>Path : Symbol(Path, Decl(templateLiteralIntersection2.ts, 0, 0))

joinedPath(`${somePath}/${somePath}`);
>joinedPath : Symbol(joinedPath, Decl(templateLiteralIntersection2.ts, 2, 36))
>somePath : Symbol(somePath, Decl(templateLiteralIntersection2.ts, 8, 13))
>somePath : Symbol(somePath, Decl(templateLiteralIntersection2.ts, 8, 13))


type StartsWithA = `a${string}`;
>StartsWithA : Symbol(StartsWithA, Decl(templateLiteralIntersection2.ts, 10, 38))

type EndsWithA = `${string}a`;
>EndsWithA : Symbol(EndsWithA, Decl(templateLiteralIntersection2.ts, 13, 32))


declare function withinAs(p: StartsWithA & EndsWithA): void;
>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30))
>p : Symbol(p, Decl(templateLiteralIntersection2.ts, 17, 26))
>StartsWithA : Symbol(StartsWithA, Decl(templateLiteralIntersection2.ts, 10, 38))
>EndsWithA : Symbol(EndsWithA, Decl(templateLiteralIntersection2.ts, 13, 32))

withinAs("");
>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30))

withinAs("a");
>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30))

withinAs("ab");
>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30))

withinAs("aba");
>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30))

withinAs("abavvvva");
>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30))

Loading