-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Allow nongeneric string mapping types to exist #47050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
361eaff
ac8596a
af74da3
7c7cddc
86fe3dd
5e8a5b5
ca66747
98eab51
f1c15d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12145,7 +12145,7 @@ namespace ts { | |
} | ||
if (t.flags & TypeFlags.StringMapping) { | ||
const constraint = getBaseConstraint((t as StringMappingType).type); | ||
return constraint ? getStringMappingType((t as StringMappingType).symbol, constraint) : stringType; | ||
return constraint && constraint !== (t as StringMappingType).type ? getStringMappingType((t as StringMappingType).symbol, constraint) : stringType; | ||
} | ||
if (t.flags & TypeFlags.IndexedAccess) { | ||
if (isMappedTypeGenericIndexedAccess(t)) { | ||
|
@@ -15317,8 +15317,11 @@ namespace ts { | |
|
||
function getStringMappingType(symbol: Symbol, type: Type): Type { | ||
return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) : | ||
isGenericIndexType(type) ? getStringMappingTypeForGenericType(symbol, type) : | ||
// Mapping<Mapping<T>> === Mapping<T> | ||
type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type : | ||
isGenericIndexType(type) || isPatternLiteralPlaceholderType(type) ? getStringMappingTypeForGenericType(symbol, isPatternLiteralPlaceholderType(type) && !(type.flags & TypeFlags.StringMapping) ? getTemplateLiteralType(["", ""], [type]) : type) : | ||
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)) : | ||
type; | ||
} | ||
|
||
|
@@ -15332,6 +15335,16 @@ namespace ts { | |
return str; | ||
} | ||
|
||
function applyTemplateStringMapping(symbol: Symbol, texts: readonly string[], types: readonly Type[]): [texts: readonly string[], types: readonly Type[]] { | ||
switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { | ||
case IntrinsicTypeKind.Uppercase: return [texts.map(t => t.toUpperCase()), types.map(t => getStringMappingType(symbol, t))]; | ||
case IntrinsicTypeKind.Lowercase: return [texts.map(t => t.toLowerCase()), types.map(t => getStringMappingType(symbol, t))]; | ||
case IntrinsicTypeKind.Capitalize: return [texts[0] === "" ? texts : [texts[0].charAt(0).toUpperCase() + texts[0].slice(1), ...texts.slice(1)], texts[0] === "" ? [getStringMappingType(symbol, types[0]), ...types.slice(1)] : types]; | ||
case IntrinsicTypeKind.Uncapitalize: return [texts[0] === "" ? texts : [texts[0].charAt(0).toLowerCase() + texts[0].slice(1), ...texts.slice(1)], texts[0] === "" ? [getStringMappingType(symbol, types[0]), ...types.slice(1)] : types]; | ||
} | ||
return [texts, types]; | ||
} | ||
|
||
function getStringMappingTypeForGenericType(symbol: Symbol, type: Type): Type { | ||
const id = `${getSymbolId(symbol)},${getTypeId(type)}`; | ||
let result = stringMappingTypes.get(id); | ||
|
@@ -15587,8 +15600,8 @@ namespace ts { | |
accessNode; | ||
} | ||
|
||
function isPatternLiteralPlaceholderType(type: Type) { | ||
return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)); | ||
function isPatternLiteralPlaceholderType(type: Type): boolean { | ||
return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || !!(type.flags & TypeFlags.StringMapping && isPatternLiteralPlaceholderType((type as StringMappingType).type)); | ||
} | ||
|
||
function isPatternLiteralType(type: Type) { | ||
|
@@ -19497,6 +19510,13 @@ namespace ts { | |
return Ternary.True; | ||
} | ||
} | ||
else if (target.flags & TypeFlags.StringMapping) { | ||
if (!(source.flags & TypeFlags.StringMapping)) { | ||
if (isUnchangedByStringMapping(target.symbol, source)) { | ||
return Ternary.True; | ||
} | ||
} | ||
} | ||
|
||
if (sourceFlags & TypeFlags.TypeVariable) { | ||
// IndexedAccess comparisons are handled above in the `targetFlags & TypeFlage.IndexedAccess` branch | ||
|
@@ -19548,7 +19568,10 @@ namespace ts { | |
} | ||
} | ||
else if (sourceFlags & TypeFlags.StringMapping) { | ||
if (targetFlags & TypeFlags.StringMapping && (source as StringMappingType).symbol === (target as StringMappingType).symbol) { | ||
if (targetFlags & TypeFlags.StringMapping) { | ||
if ((source as StringMappingType).symbol !== (target as StringMappingType).symbol) { | ||
return Ternary.False; | ||
} | ||
if (result = isRelatedTo((source as StringMappingType).type, (target as StringMappingType).type, RecursionFlags.Both, reportErrors)) { | ||
resetErrorInfo(saveErrorInfo); | ||
return result; | ||
|
@@ -20502,7 +20525,7 @@ namespace ts { | |
} | ||
} | ||
|
||
return isUnitType(type) || !!(type.flags & TypeFlags.TemplateLiteral); | ||
return isUnitType(type) || !!(type.flags & TypeFlags.TemplateLiteral) || !!(type.flags & TypeFlags.StringMapping); | ||
} | ||
|
||
function getExactOptionalUnassignableProperties(source: Type, target: Type) { | ||
|
@@ -22052,6 +22075,10 @@ namespace ts { | |
return success && result === SyntaxKind.BigIntLiteral && scanner.getTextPos() === (s.length + 1) && !(flags & TokenFlags.ContainsSeparator); | ||
} | ||
|
||
function isUnchangedByStringMapping(symbol: Symbol, value: Type) { | ||
return value === getStringMappingType(symbol, value); | ||
} | ||
|
||
function isValidTypeForTemplateLiteralPlaceholder(source: Type, target: Type): boolean { | ||
if (source === target || target.flags & (TypeFlags.Any | TypeFlags.String)) { | ||
return true; | ||
|
@@ -22060,7 +22087,8 @@ namespace ts { | |
const value = (source as StringLiteralType).value; | ||
return !!(target.flags & TypeFlags.Number && value !== "" && isFinite(+value) || | ||
target.flags & TypeFlags.BigInt && value !== "" && isValidBigIntString(value) || | ||
target.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) && value === (target as IntrinsicType).intrinsicName); | ||
target.flags & (TypeFlags.BooleanLiteral | TypeFlags.Nullable) && value === (target as IntrinsicType).intrinsicName || | ||
target.flags & TypeFlags.StringMapping && isUnchangedByStringMapping(target.symbol, getStringLiteralType(value))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't target be a string mapping over pretty much anything? That doesn't seem right. |
||
} | ||
if (source.flags & TypeFlags.TemplateLiteral) { | ||
const texts = (source as TemplateLiteralType).texts; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,11 +7,11 @@ type TU2 = Uppercase<'foo' | 'bar'>; // "FOO" | "BAR" | |
>TU2 : Symbol(TU2, Decl(intrinsicTypes.ts, 0, 30)) | ||
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type TU3 = Uppercase<string>; // string | ||
type TU3 = Uppercase<string>; // Uppercase<string> | ||
>TU3 : Symbol(TU3, Decl(intrinsicTypes.ts, 1, 36)) | ||
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type TU4 = Uppercase<any>; // any | ||
type TU4 = Uppercase<any>; // Uppercase<`${any}`> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason we can't just produce There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Iirc, mostly just because, as written, nongeneric string mapping types can only exist over |
||
>TU4 : Symbol(TU4, Decl(intrinsicTypes.ts, 2, 29)) | ||
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --)) | ||
|
||
|
@@ -31,11 +31,11 @@ type TL2 = Lowercase<'FOO' | 'BAR'>; // "foo" | "bar" | |
>TL2 : Symbol(TL2, Decl(intrinsicTypes.ts, 7, 30)) | ||
>Lowercase : Symbol(Lowercase, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type TL3 = Lowercase<string>; // string | ||
type TL3 = Lowercase<string>; // Lowercase<string> | ||
>TL3 : Symbol(TL3, Decl(intrinsicTypes.ts, 8, 36)) | ||
>Lowercase : Symbol(Lowercase, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type TL4 = Lowercase<any>; // any | ||
type TL4 = Lowercase<any>; // Lowercase<`${any}`> | ||
>TL4 : Symbol(TL4, Decl(intrinsicTypes.ts, 9, 29)) | ||
>Lowercase : Symbol(Lowercase, Decl(lib.es5.d.ts, --, --)) | ||
|
||
|
@@ -55,11 +55,11 @@ type TC2 = Capitalize<'foo' | 'bar'>; // "Foo" | "Bar" | |
>TC2 : Symbol(TC2, Decl(intrinsicTypes.ts, 14, 31)) | ||
>Capitalize : Symbol(Capitalize, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type TC3 = Capitalize<string>; // string | ||
type TC3 = Capitalize<string>; // Capitalize<string> | ||
>TC3 : Symbol(TC3, Decl(intrinsicTypes.ts, 15, 37)) | ||
>Capitalize : Symbol(Capitalize, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type TC4 = Capitalize<any>; // any | ||
type TC4 = Capitalize<any>; // Capitalize<`${any}`> | ||
>TC4 : Symbol(TC4, Decl(intrinsicTypes.ts, 16, 30)) | ||
>Capitalize : Symbol(Capitalize, Decl(lib.es5.d.ts, --, --)) | ||
|
||
|
@@ -79,11 +79,11 @@ type TN2 = Uncapitalize<'Foo' | 'Bar'>; // "foo" | "bar" | |
>TN2 : Symbol(TN2, Decl(intrinsicTypes.ts, 21, 33)) | ||
>Uncapitalize : Symbol(Uncapitalize, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type TN3 = Uncapitalize<string>; // string | ||
type TN3 = Uncapitalize<string>; // Uncapitalize<string> | ||
>TN3 : Symbol(TN3, Decl(intrinsicTypes.ts, 22, 39)) | ||
>Uncapitalize : Symbol(Uncapitalize, Decl(lib.es5.d.ts, --, --)) | ||
|
||
type TN4 = Uncapitalize<any>; // any | ||
type TN4 = Uncapitalize<any>; // Uncapitalize<`${any}`> | ||
>TN4 : Symbol(TN4, Decl(intrinsicTypes.ts, 23, 32)) | ||
>Uncapitalize : Symbol(Uncapitalize, Decl(lib.es5.d.ts, --, --)) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check seems too broad.