mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-08 12:55:49 -05:00
Allow nongeneric string mapping types to exist (#47050)
* Allow nongeneric string mapping types to exist * Accept baseline * Recusive membership testing function * Fix lint * Add @DanielRosenwasser's comment
This commit is contained in:
@@ -12182,7 +12182,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)) {
|
||||
@@ -15381,8 +15381,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;
|
||||
}
|
||||
|
||||
@@ -15396,6 +15399,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);
|
||||
@@ -15651,8 +15664,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) {
|
||||
@@ -19613,6 +19626,13 @@ namespace ts {
|
||||
return Ternary.True;
|
||||
}
|
||||
}
|
||||
else if (target.flags & TypeFlags.StringMapping) {
|
||||
if (!(source.flags & TypeFlags.StringMapping)) {
|
||||
if (isMemberOfStringMapping(source, target)) {
|
||||
return Ternary.True;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceFlags & TypeFlags.TypeVariable) {
|
||||
// IndexedAccess comparisons are handled above in the `targetFlags & TypeFlage.IndexedAccess` branch
|
||||
@@ -19657,7 +19677,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;
|
||||
@@ -20611,7 +20634,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) {
|
||||
@@ -22171,6 +22194,32 @@ namespace ts {
|
||||
&& (!roundTripOnly || s === pseudoBigIntToString({ negative, base10Value: parsePseudoBigInt(scanner.getTokenValue()) }));
|
||||
}
|
||||
|
||||
function isMemberOfStringMapping(source: Type, target: Type): boolean {
|
||||
if (target.flags & (TypeFlags.String | TypeFlags.AnyOrUnknown)) {
|
||||
return true;
|
||||
}
|
||||
if (target.flags & TypeFlags.TemplateLiteral) {
|
||||
return isTypeAssignableTo(source, target);
|
||||
}
|
||||
if (target.flags & TypeFlags.StringMapping) {
|
||||
// We need to see whether applying the same mappings of the target
|
||||
// onto the source would produce an identical type *and* that
|
||||
// it's compatible with the inner-most non-string-mapped type.
|
||||
//
|
||||
// The intuition here is that if same mappings don't affect the source at all,
|
||||
// and the source is compatible with the unmapped target, then they must
|
||||
// still reside in the same domain.
|
||||
const mappingStack = [];
|
||||
while (target.flags & TypeFlags.StringMapping) {
|
||||
mappingStack.unshift(target.symbol);
|
||||
target = (target as StringMappingType).type;
|
||||
}
|
||||
const mappedSource = reduceLeft(mappingStack, (memo, value) => getStringMappingType(value, memo), source);
|
||||
return mappedSource === source && isMemberOfStringMapping(source, target);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isValidTypeForTemplateLiteralPlaceholder(source: Type, target: Type): boolean {
|
||||
if (source === target || target.flags & (TypeFlags.Any | TypeFlags.String)) {
|
||||
return true;
|
||||
@@ -22179,7 +22228,8 @@ namespace ts {
|
||||
const value = (source as StringLiteralType).value;
|
||||
return !!(target.flags & TypeFlags.Number && isValidNumberString(value, /*roundTripOnly*/ false) ||
|
||||
target.flags & TypeFlags.BigInt && isValidBigIntString(value, /*roundTripOnly*/ false) ||
|
||||
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 && isMemberOfStringMapping(getStringLiteralType(value), target));
|
||||
}
|
||||
if (source.flags & TypeFlags.TemplateLiteral) {
|
||||
const texts = (source as TemplateLiteralType).texts;
|
||||
|
||||
Reference in New Issue
Block a user