mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-24 04:30:53 -06:00
Properly defer resolution of mapped types with generic as clauses (#51050)
* Fix isGenericMappedType, getTemplateLiteralType, getStringMappingType * Accept new baselines * Add regression tests * Fix comment
This commit is contained in:
parent
42b1049aee
commit
d06a592d02
@ -12073,7 +12073,20 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isGenericMappedType(type: Type): type is MappedType {
|
||||
return !!(getObjectFlags(type) & ObjectFlags.Mapped) && isGenericIndexType(getConstraintTypeFromMappedType(type as MappedType));
|
||||
if (getObjectFlags(type) & ObjectFlags.Mapped) {
|
||||
const constraint = getConstraintTypeFromMappedType(type as MappedType);
|
||||
if (isGenericIndexType(constraint)) {
|
||||
return true;
|
||||
}
|
||||
// A mapped type is generic if the 'as' clause references generic types other than the iteration type.
|
||||
// To determine this, we substitute the constraint type (that we now know isn't generic) for the iteration
|
||||
// type and check whether the resulting type is generic.
|
||||
const nameType = getNameTypeFromMappedType(type as MappedType);
|
||||
if (nameType && isGenericIndexType(instantiateType(nameType, makeUnaryTypeMapper(getTypeParameterFromMappedType(type as MappedType), constraint)))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
|
||||
@ -15638,8 +15651,14 @@ namespace ts {
|
||||
return getStringLiteralType(text);
|
||||
}
|
||||
newTexts.push(text);
|
||||
if (every(newTexts, t => t === "") && every(newTypes, t => !!(t.flags & TypeFlags.String))) {
|
||||
return stringType;
|
||||
if (every(newTexts, t => t === "")) {
|
||||
if (every(newTypes, t => !!(t.flags & TypeFlags.String))) {
|
||||
return stringType;
|
||||
}
|
||||
// Normalize `${Mapping<xxx>}` into Mapping<xxx>
|
||||
if (newTypes.length === 1 && isPatternLiteralType(newTypes[0])) {
|
||||
return newTypes[0];
|
||||
}
|
||||
}
|
||||
const id = `${getTypeListId(newTypes)}|${map(newTexts, t => t.length).join(",")}|${newTexts.join("")}`;
|
||||
let type = templateLiteralTypes.get(id);
|
||||
@ -15698,11 +15717,13 @@ namespace ts {
|
||||
|
||||
function getStringMappingType(symbol: Symbol, type: Type): Type {
|
||||
return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) :
|
||||
// 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)) :
|
||||
// Mapping<Mapping<T>> === Mapping<T>
|
||||
type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type :
|
||||
type.flags & (TypeFlags.Any | TypeFlags.String || type.flags & TypeFlags.StringMapping) || isGenericIndexType(type) ? getStringMappingTypeForGenericType(symbol, type) :
|
||||
// This handles Mapping<`${number}`> and Mapping<`${bigint}`>
|
||||
isPatternLiteralPlaceholderType(type) ? getStringMappingTypeForGenericType(symbol, getTemplateLiteralType(["", ""], [type])) :
|
||||
type;
|
||||
}
|
||||
|
||||
@ -16000,11 +16021,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
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));
|
||||
return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || isPatternLiteralType(type);
|
||||
}
|
||||
|
||||
function isPatternLiteralType(type: Type) {
|
||||
return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType);
|
||||
return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType) ||
|
||||
!!(type.flags & TypeFlags.StringMapping) && isPatternLiteralPlaceholderType((type as StringMappingType).type);
|
||||
}
|
||||
|
||||
function isGenericType(type: Type): boolean {
|
||||
@ -22559,7 +22581,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isMemberOfStringMapping(source: Type, target: Type): boolean {
|
||||
if (target.flags & (TypeFlags.String | TypeFlags.AnyOrUnknown)) {
|
||||
if (target.flags & (TypeFlags.String | TypeFlags.Any)) {
|
||||
return true;
|
||||
}
|
||||
if (target.flags & TypeFlags.TemplateLiteral) {
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
tests/cases/compiler/genericMappedTypeAsClause.ts(11,36): error TS2322: Type 'string' is not assignable to type 'number'.
|
||||
tests/cases/compiler/genericMappedTypeAsClause.ts(14,11): error TS2322: Type 'number' is not assignable to type 'MappedModel<T>'.
|
||||
tests/cases/compiler/genericMappedTypeAsClause.ts(15,11): error TS2322: Type 'string' is not assignable to type 'MappedModel<T>'.
|
||||
tests/cases/compiler/genericMappedTypeAsClause.ts(16,11): error TS2322: Type 'number[]' is not assignable to type 'MappedModel<T>'.
|
||||
tests/cases/compiler/genericMappedTypeAsClause.ts(17,11): error TS2322: Type 'boolean' is not assignable to type 'MappedModel<T>'.
|
||||
tests/cases/compiler/genericMappedTypeAsClause.ts(18,34): error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'MappedModel<T>'.
|
||||
Object literal may only specify known properties, and 'a' does not exist in type 'MappedModel<T>'.
|
||||
tests/cases/compiler/genericMappedTypeAsClause.ts(19,11): error TS2322: Type 'undefined' is not assignable to type 'MappedModel<T>'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/genericMappedTypeAsClause.ts (7 errors) ====
|
||||
type Model = {
|
||||
a: string;
|
||||
b: number;
|
||||
};
|
||||
|
||||
type MappedModel<Suffix extends string> = {
|
||||
[K in keyof Model as `${K}${Suffix}`]: Model[K];
|
||||
};
|
||||
|
||||
const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 };
|
||||
const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error
|
||||
~~~~
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'number'.
|
||||
!!! related TS6500 tests/cases/compiler/genericMappedTypeAsClause.ts:6:43: The expected type comes from property 'bFoo' which is declared here on type 'MappedModel<"Foo">'
|
||||
|
||||
function f1<T extends string>() {
|
||||
const x1: MappedModel<T> = 42; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'number' is not assignable to type 'MappedModel<T>'.
|
||||
const x2: MappedModel<T> = 'test'; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'MappedModel<T>'.
|
||||
const x3: MappedModel<T> = [1, 2, 3]; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'number[]' is not assignable to type 'MappedModel<T>'.
|
||||
const x4: MappedModel<T> = false; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'boolean' is not assignable to type 'MappedModel<T>'.
|
||||
const x5: MappedModel<T> = { a: 'bar', b: 42 }; // Error
|
||||
~~~~~~~~
|
||||
!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'MappedModel<T>'.
|
||||
!!! error TS2322: Object literal may only specify known properties, and 'a' does not exist in type 'MappedModel<T>'.
|
||||
const x6: MappedModel<T> = undefined; // Error
|
||||
~~
|
||||
!!! error TS2322: Type 'undefined' is not assignable to type 'MappedModel<T>'.
|
||||
}
|
||||
|
||||
35
tests/baselines/reference/genericMappedTypeAsClause.js
Normal file
35
tests/baselines/reference/genericMappedTypeAsClause.js
Normal file
@ -0,0 +1,35 @@
|
||||
//// [genericMappedTypeAsClause.ts]
|
||||
type Model = {
|
||||
a: string;
|
||||
b: number;
|
||||
};
|
||||
|
||||
type MappedModel<Suffix extends string> = {
|
||||
[K in keyof Model as `${K}${Suffix}`]: Model[K];
|
||||
};
|
||||
|
||||
const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 };
|
||||
const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error
|
||||
|
||||
function f1<T extends string>() {
|
||||
const x1: MappedModel<T> = 42; // Error
|
||||
const x2: MappedModel<T> = 'test'; // Error
|
||||
const x3: MappedModel<T> = [1, 2, 3]; // Error
|
||||
const x4: MappedModel<T> = false; // Error
|
||||
const x5: MappedModel<T> = { a: 'bar', b: 42 }; // Error
|
||||
const x6: MappedModel<T> = undefined; // Error
|
||||
}
|
||||
|
||||
|
||||
//// [genericMappedTypeAsClause.js]
|
||||
"use strict";
|
||||
var foo1 = { aFoo: 'test', bFoo: 42 };
|
||||
var foo2 = { bFoo: 'bar' }; // Error
|
||||
function f1() {
|
||||
var x1 = 42; // Error
|
||||
var x2 = 'test'; // Error
|
||||
var x3 = [1, 2, 3]; // Error
|
||||
var x4 = false; // Error
|
||||
var x5 = { a: 'bar', b: 42 }; // Error
|
||||
var x6 = undefined; // Error
|
||||
}
|
||||
75
tests/baselines/reference/genericMappedTypeAsClause.symbols
Normal file
75
tests/baselines/reference/genericMappedTypeAsClause.symbols
Normal file
@ -0,0 +1,75 @@
|
||||
=== tests/cases/compiler/genericMappedTypeAsClause.ts ===
|
||||
type Model = {
|
||||
>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0))
|
||||
|
||||
a: string;
|
||||
>a : Symbol(a, Decl(genericMappedTypeAsClause.ts, 0, 14))
|
||||
|
||||
b: number;
|
||||
>b : Symbol(b, Decl(genericMappedTypeAsClause.ts, 1, 14))
|
||||
|
||||
};
|
||||
|
||||
type MappedModel<Suffix extends string> = {
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>Suffix : Symbol(Suffix, Decl(genericMappedTypeAsClause.ts, 5, 17))
|
||||
|
||||
[K in keyof Model as `${K}${Suffix}`]: Model[K];
|
||||
>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5))
|
||||
>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0))
|
||||
>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5))
|
||||
>Suffix : Symbol(Suffix, Decl(genericMappedTypeAsClause.ts, 5, 17))
|
||||
>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0))
|
||||
>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5))
|
||||
|
||||
};
|
||||
|
||||
const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 };
|
||||
>foo1 : Symbol(foo1, Decl(genericMappedTypeAsClause.ts, 9, 5))
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>aFoo : Symbol(aFoo, Decl(genericMappedTypeAsClause.ts, 9, 34))
|
||||
>bFoo : Symbol(bFoo, Decl(genericMappedTypeAsClause.ts, 9, 48))
|
||||
|
||||
const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error
|
||||
>foo2 : Symbol(foo2, Decl(genericMappedTypeAsClause.ts, 10, 5))
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>bFoo : Symbol(bFoo, Decl(genericMappedTypeAsClause.ts, 10, 34))
|
||||
|
||||
function f1<T extends string>() {
|
||||
>f1 : Symbol(f1, Decl(genericMappedTypeAsClause.ts, 10, 49))
|
||||
>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12))
|
||||
|
||||
const x1: MappedModel<T> = 42; // Error
|
||||
>x1 : Symbol(x1, Decl(genericMappedTypeAsClause.ts, 13, 9))
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12))
|
||||
|
||||
const x2: MappedModel<T> = 'test'; // Error
|
||||
>x2 : Symbol(x2, Decl(genericMappedTypeAsClause.ts, 14, 9))
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12))
|
||||
|
||||
const x3: MappedModel<T> = [1, 2, 3]; // Error
|
||||
>x3 : Symbol(x3, Decl(genericMappedTypeAsClause.ts, 15, 9))
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12))
|
||||
|
||||
const x4: MappedModel<T> = false; // Error
|
||||
>x4 : Symbol(x4, Decl(genericMappedTypeAsClause.ts, 16, 9))
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12))
|
||||
|
||||
const x5: MappedModel<T> = { a: 'bar', b: 42 }; // Error
|
||||
>x5 : Symbol(x5, Decl(genericMappedTypeAsClause.ts, 17, 9))
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12))
|
||||
>a : Symbol(a, Decl(genericMappedTypeAsClause.ts, 17, 32))
|
||||
>b : Symbol(b, Decl(genericMappedTypeAsClause.ts, 17, 42))
|
||||
|
||||
const x6: MappedModel<T> = undefined; // Error
|
||||
>x6 : Symbol(x6, Decl(genericMappedTypeAsClause.ts, 18, 9))
|
||||
>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2))
|
||||
>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12))
|
||||
>undefined : Symbol(undefined)
|
||||
}
|
||||
|
||||
67
tests/baselines/reference/genericMappedTypeAsClause.types
Normal file
67
tests/baselines/reference/genericMappedTypeAsClause.types
Normal file
@ -0,0 +1,67 @@
|
||||
=== tests/cases/compiler/genericMappedTypeAsClause.ts ===
|
||||
type Model = {
|
||||
>Model : { a: string; b: number; }
|
||||
|
||||
a: string;
|
||||
>a : string
|
||||
|
||||
b: number;
|
||||
>b : number
|
||||
|
||||
};
|
||||
|
||||
type MappedModel<Suffix extends string> = {
|
||||
>MappedModel : MappedModel<Suffix>
|
||||
|
||||
[K in keyof Model as `${K}${Suffix}`]: Model[K];
|
||||
};
|
||||
|
||||
const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 };
|
||||
>foo1 : MappedModel<"Foo">
|
||||
>{ aFoo: 'test', bFoo: 42 } : { aFoo: string; bFoo: number; }
|
||||
>aFoo : string
|
||||
>'test' : "test"
|
||||
>bFoo : number
|
||||
>42 : 42
|
||||
|
||||
const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error
|
||||
>foo2 : MappedModel<"Foo">
|
||||
>{ bFoo: 'bar' } : { bFoo: string; }
|
||||
>bFoo : string
|
||||
>'bar' : "bar"
|
||||
|
||||
function f1<T extends string>() {
|
||||
>f1 : <T extends string>() => void
|
||||
|
||||
const x1: MappedModel<T> = 42; // Error
|
||||
>x1 : MappedModel<T>
|
||||
>42 : 42
|
||||
|
||||
const x2: MappedModel<T> = 'test'; // Error
|
||||
>x2 : MappedModel<T>
|
||||
>'test' : "test"
|
||||
|
||||
const x3: MappedModel<T> = [1, 2, 3]; // Error
|
||||
>x3 : MappedModel<T>
|
||||
>[1, 2, 3] : number[]
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>3 : 3
|
||||
|
||||
const x4: MappedModel<T> = false; // Error
|
||||
>x4 : MappedModel<T>
|
||||
>false : false
|
||||
|
||||
const x5: MappedModel<T> = { a: 'bar', b: 42 }; // Error
|
||||
>x5 : MappedModel<T>
|
||||
>{ a: 'bar', b: 42 } : { a: string; b: number; }
|
||||
>a : string
|
||||
>'bar' : "bar"
|
||||
>b : number
|
||||
>42 : 42
|
||||
|
||||
const x6: MappedModel<T> = undefined; // Error
|
||||
>x6 : MappedModel<T>
|
||||
>undefined : undefined
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ type TU3 = Uppercase<string>; // Uppercase<string>
|
||||
>TU3 : Uppercase<string>
|
||||
|
||||
type TU4 = Uppercase<any>; // Uppercase<`${any}`>
|
||||
>TU4 : Uppercase<`${any}`>
|
||||
>TU4 : Uppercase<any>
|
||||
|
||||
type TU5 = Uppercase<never>; // never
|
||||
>TU5 : never
|
||||
@ -27,7 +27,7 @@ type TL3 = Lowercase<string>; // Lowercase<string>
|
||||
>TL3 : Lowercase<string>
|
||||
|
||||
type TL4 = Lowercase<any>; // Lowercase<`${any}`>
|
||||
>TL4 : Lowercase<`${any}`>
|
||||
>TL4 : Lowercase<any>
|
||||
|
||||
type TL5 = Lowercase<never>; // never
|
||||
>TL5 : never
|
||||
@ -45,7 +45,7 @@ type TC3 = Capitalize<string>; // Capitalize<string>
|
||||
>TC3 : Capitalize<string>
|
||||
|
||||
type TC4 = Capitalize<any>; // Capitalize<`${any}`>
|
||||
>TC4 : Capitalize<`${any}`>
|
||||
>TC4 : Capitalize<any>
|
||||
|
||||
type TC5 = Capitalize<never>; // never
|
||||
>TC5 : never
|
||||
@ -63,7 +63,7 @@ type TN3 = Uncapitalize<string>; // Uncapitalize<string>
|
||||
>TN3 : Uncapitalize<string>
|
||||
|
||||
type TN4 = Uncapitalize<any>; // Uncapitalize<`${any}`>
|
||||
>TN4 : Uncapitalize<`${any}`>
|
||||
>TN4 : Uncapitalize<any>
|
||||
|
||||
type TN5 = Uncapitalize<never>; // never
|
||||
>TN5 : never
|
||||
@ -72,13 +72,13 @@ type TN6 = Uncapitalize<42>; // Error
|
||||
>TN6 : 42
|
||||
|
||||
type TX1<S extends string> = Uppercase<`aB${S}`>;
|
||||
>TX1 : Uppercase<`aB${S}`>
|
||||
>TX1 : `AB${Uppercase<S>}`
|
||||
|
||||
type TX2 = TX1<'xYz'>; // "ABXYZ"
|
||||
>TX2 : "ABXYZ"
|
||||
|
||||
type TX3<S extends string> = Lowercase<`aB${S}`>;
|
||||
>TX3 : Lowercase<`aB${S}`>
|
||||
>TX3 : `ab${Lowercase<S>}`
|
||||
|
||||
type TX4 = TX3<'xYz'>; // "abxyz"
|
||||
>TX4 : "abxyz"
|
||||
|
||||
@ -61,7 +61,7 @@ type TD1 = DoubleProp<{ a: string, b: number }>; // { a1: string, a2: string, b
|
||||
>b : number
|
||||
|
||||
type TD2 = keyof TD1; // 'a1' | 'a2' | 'b1' | 'b2'
|
||||
>TD2 : "a1" | "a2" | "b1" | "b2"
|
||||
>TD2 : "a1" | "b1" | "a2" | "b2"
|
||||
|
||||
type TD3<U> = keyof DoubleProp<U>; // `${keyof U & string}1` | `${keyof U & string}2`
|
||||
>TD3 : `${keyof U & string}1` | `${keyof U & string}2`
|
||||
|
||||
@ -12,7 +12,7 @@ type T3<T extends string> = string & `${T}`; // `${T}
|
||||
>T3 : `${T}`
|
||||
|
||||
type T4<T extends string> = string & `${Capitalize<`${T}`>}`; // `${Capitalize<T>}`
|
||||
>T4 : `${Capitalize<`${T}`>}`
|
||||
>T4 : `${Capitalize<T>}`
|
||||
|
||||
function f1(a: boolean[], x: `${number}`) {
|
||||
>f1 : (a: boolean[], x: `${number}`) => void
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(7,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<Lowercase<string>>'.
|
||||
tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(15,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'.
|
||||
tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(16,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'.
|
||||
tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(15,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<Lowercase<`${number}`>>'.
|
||||
tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(16,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<Lowercase<`${number}`>>'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts (3 errors) ====
|
||||
@ -22,7 +22,7 @@ tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(
|
||||
// bad
|
||||
y = "a";
|
||||
~
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'Uppercase<Lowercase<`${number}`>>'.
|
||||
y = "A";
|
||||
~
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'Uppercase<Lowercase<`${number}`>>'.
|
||||
@ -15,22 +15,22 @@ x = "a";
|
||||
>"a" : "a"
|
||||
|
||||
declare var y: Uppercase<Lowercase<`${number}`>>;
|
||||
>y : Uppercase<`${Lowercase<`${number}`>}`>
|
||||
>y : Uppercase<Lowercase<`${number}`>>
|
||||
|
||||
// good
|
||||
y = "1";
|
||||
>y = "1" : "1"
|
||||
>y : Uppercase<`${Lowercase<`${number}`>}`>
|
||||
>y : Uppercase<Lowercase<`${number}`>>
|
||||
>"1" : "1"
|
||||
|
||||
// bad
|
||||
y = "a";
|
||||
>y = "a" : "a"
|
||||
>y : Uppercase<`${Lowercase<`${number}`>}`>
|
||||
>y : Uppercase<Lowercase<`${number}`>>
|
||||
>"a" : "a"
|
||||
|
||||
y = "A";
|
||||
>y = "A" : "A"
|
||||
>y : Uppercase<`${Lowercase<`${number}`>}`>
|
||||
>y : Uppercase<Lowercase<`${number}`>>
|
||||
>"A" : "A"
|
||||
|
||||
|
||||
@ -568,8 +568,8 @@ function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>,
|
||||
>ft1 : <T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) => void
|
||||
>t : T
|
||||
>u : Uppercase<T>
|
||||
>u1 : Uppercase<`1.${T}.3`>
|
||||
>u2 : Uppercase<`1.${T}.4`>
|
||||
>u1 : `1.${Uppercase<T>}.3`
|
||||
>u2 : `1.${Uppercase<T>}.4`
|
||||
|
||||
spread(`1.${t}.3`, `1.${t}.4`);
|
||||
>spread(`1.${t}.3`, `1.${t}.4`) : `1.${T}.3` | `1.${T}.4`
|
||||
@ -588,9 +588,9 @@ function ft1<T extends string>(t: T, u: Uppercase<T>, u1: Uppercase<`1.${T}.3`>,
|
||||
>u : Uppercase<T>
|
||||
|
||||
spread(u1, u2);
|
||||
>spread(u1, u2) : Uppercase<`1.${T}.3`> | Uppercase<`1.${T}.4`>
|
||||
>spread(u1, u2) : `1.${Uppercase<T>}.3` | `1.${Uppercase<T>}.4`
|
||||
>spread : <P extends `${string}.${string}.${string}`>(...args: P[]) => P
|
||||
>u1 : Uppercase<`1.${T}.3`>
|
||||
>u2 : Uppercase<`1.${T}.4`>
|
||||
>u1 : `1.${Uppercase<T>}.3`
|
||||
>u2 : `1.${Uppercase<T>}.4`
|
||||
}
|
||||
|
||||
|
||||
22
tests/cases/compiler/genericMappedTypeAsClause.ts
Normal file
22
tests/cases/compiler/genericMappedTypeAsClause.ts
Normal file
@ -0,0 +1,22 @@
|
||||
// @strict: true
|
||||
|
||||
type Model = {
|
||||
a: string;
|
||||
b: number;
|
||||
};
|
||||
|
||||
type MappedModel<Suffix extends string> = {
|
||||
[K in keyof Model as `${K}${Suffix}`]: Model[K];
|
||||
};
|
||||
|
||||
const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 };
|
||||
const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error
|
||||
|
||||
function f1<T extends string>() {
|
||||
const x1: MappedModel<T> = 42; // Error
|
||||
const x2: MappedModel<T> = 'test'; // Error
|
||||
const x3: MappedModel<T> = [1, 2, 3]; // Error
|
||||
const x4: MappedModel<T> = false; // Error
|
||||
const x5: MappedModel<T> = { a: 'bar', b: 42 }; // Error
|
||||
const x6: MappedModel<T> = undefined; // Error
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user