mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
More complete check in isConstTypeVariable (#53341)
This commit is contained in:
parent
b40385b595
commit
3f675b60bf
@ -13557,10 +13557,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined;
|
||||
}
|
||||
|
||||
function isConstTypeVariable(type: Type): boolean {
|
||||
return !!(type.flags & TypeFlags.TypeParameter && some((type as TypeParameter).symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.Const)) ||
|
||||
isGenericTupleType(type) && findIndex(getTypeArguments(type), (t, i) => !!(type.target.elementFlags[i] & ElementFlags.Variadic) && isConstTypeVariable(t)) >= 0 ||
|
||||
type.flags & TypeFlags.IndexedAccess && isConstTypeVariable((type as IndexedAccessType).objectType));
|
||||
function isConstTypeVariable(type: Type | undefined): boolean {
|
||||
return !!(type && (
|
||||
type.flags & TypeFlags.TypeParameter && some((type as TypeParameter).symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.Const)) ||
|
||||
type.flags & TypeFlags.Union && some((type as UnionType).types, isConstTypeVariable) ||
|
||||
type.flags & TypeFlags.IndexedAccess && isConstTypeVariable((type as IndexedAccessType).objectType) ||
|
||||
type.flags & TypeFlags.Conditional && isConstTypeVariable(getConstraintOfConditionalType(type as ConditionalType)) ||
|
||||
type.flags & TypeFlags.Substitution && isConstTypeVariable((type as SubstitutionType).baseType) ||
|
||||
isGenericTupleType(type) && findIndex(getTypeArguments(type), (t, i) => !!(type.target.elementFlags[i] & ElementFlags.Variadic) && isConstTypeVariable(t)) >= 0));
|
||||
}
|
||||
|
||||
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
|
||||
@ -37385,16 +37389,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const parent = node.parent;
|
||||
return isAssertionExpression(parent) && isConstTypeReference(parent.type) ||
|
||||
isJSDocTypeAssertion(parent) && isConstTypeReference(getJSDocTypeAssertionType(parent)) ||
|
||||
isValidConstAssertionArgument(node) && isConstTypeParameterContext(node) ||
|
||||
isValidConstAssertionArgument(node) && isConstTypeVariable(getContextualType(node, ContextFlags.None)) ||
|
||||
(isParenthesizedExpression(parent) || isArrayLiteralExpression(parent) || isSpreadElement(parent)) && isConstContext(parent) ||
|
||||
(isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent);
|
||||
}
|
||||
|
||||
function isConstTypeParameterContext(node: Expression) {
|
||||
const contextualType = getContextualType(node, ContextFlags.None);
|
||||
return !!contextualType && someType(contextualType, isConstTypeVariable);
|
||||
}
|
||||
|
||||
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, forceTuple?: boolean): Type {
|
||||
const type = checkExpression(node, checkMode, forceTuple);
|
||||
return isConstContext(node) || isCommonJsExportedExpression(node) ? getRegularTypeOfLiteralType(type) :
|
||||
|
||||
@ -91,4 +91,18 @@ tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterCon
|
||||
declare function inners2<const T extends readonly any[]>(args: readonly [unknown, ...T, unknown]): T;
|
||||
|
||||
const test2 = inners2([1,2,3,4,5]);
|
||||
|
||||
// Repro from #53307
|
||||
|
||||
type NotEmpty<T extends Record<string, any>> = keyof T extends never ? never : T;
|
||||
|
||||
const thing = <const O extends Record<string, any>>(o: NotEmpty<O>) => o;
|
||||
|
||||
const t = thing({ foo: '' }); // readonly { foo: "" }
|
||||
|
||||
type NotEmptyMapped<T extends Record<string, any>> = keyof T extends never ? never : { [K in keyof T]: T[K] };
|
||||
|
||||
const thingMapped = <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => o;
|
||||
|
||||
const tMapped = thingMapped({ foo: '' }); // { foo: "" }
|
||||
|
||||
@ -83,6 +83,20 @@ const test = inners(1,2,3,4,5);
|
||||
declare function inners2<const T extends readonly any[]>(args: readonly [unknown, ...T, unknown]): T;
|
||||
|
||||
const test2 = inners2([1,2,3,4,5]);
|
||||
|
||||
// Repro from #53307
|
||||
|
||||
type NotEmpty<T extends Record<string, any>> = keyof T extends never ? never : T;
|
||||
|
||||
const thing = <const O extends Record<string, any>>(o: NotEmpty<O>) => o;
|
||||
|
||||
const t = thing({ foo: '' }); // readonly { foo: "" }
|
||||
|
||||
type NotEmptyMapped<T extends Record<string, any>> = keyof T extends never ? never : { [K in keyof T]: T[K] };
|
||||
|
||||
const thingMapped = <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => o;
|
||||
|
||||
const tMapped = thingMapped({ foo: '' }); // { foo: "" }
|
||||
|
||||
|
||||
//// [typeParameterConstModifiers.js]
|
||||
@ -130,3 +144,7 @@ function set(obj, path, value) { }
|
||||
set(obj, ['a', 'b', 'c'], value);
|
||||
var test = inners(1, 2, 3, 4, 5);
|
||||
var test2 = inners2([1, 2, 3, 4, 5]);
|
||||
var thing = function (o) { return o; };
|
||||
var t = thing({ foo: '' }); // readonly { foo: "" }
|
||||
var thingMapped = function (o) { return o; };
|
||||
var tMapped = thingMapped({ foo: '' }); // { foo: "" }
|
||||
|
||||
@ -304,3 +304,50 @@ const test2 = inners2([1,2,3,4,5]);
|
||||
>test2 : Symbol(test2, Decl(typeParameterConstModifiers.ts, 83, 5))
|
||||
>inners2 : Symbol(inners2, Decl(typeParameterConstModifiers.ts, 79, 31))
|
||||
|
||||
// Repro from #53307
|
||||
|
||||
type NotEmpty<T extends Record<string, any>> = keyof T extends never ? never : T;
|
||||
>NotEmpty : Symbol(NotEmpty, Decl(typeParameterConstModifiers.ts, 83, 35))
|
||||
>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 87, 14))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 87, 14))
|
||||
>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 87, 14))
|
||||
|
||||
const thing = <const O extends Record<string, any>>(o: NotEmpty<O>) => o;
|
||||
>thing : Symbol(thing, Decl(typeParameterConstModifiers.ts, 89, 5))
|
||||
>O : Symbol(O, Decl(typeParameterConstModifiers.ts, 89, 15))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>o : Symbol(o, Decl(typeParameterConstModifiers.ts, 89, 52))
|
||||
>NotEmpty : Symbol(NotEmpty, Decl(typeParameterConstModifiers.ts, 83, 35))
|
||||
>O : Symbol(O, Decl(typeParameterConstModifiers.ts, 89, 15))
|
||||
>o : Symbol(o, Decl(typeParameterConstModifiers.ts, 89, 52))
|
||||
|
||||
const t = thing({ foo: '' }); // readonly { foo: "" }
|
||||
>t : Symbol(t, Decl(typeParameterConstModifiers.ts, 91, 5))
|
||||
>thing : Symbol(thing, Decl(typeParameterConstModifiers.ts, 89, 5))
|
||||
>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 91, 17))
|
||||
|
||||
type NotEmptyMapped<T extends Record<string, any>> = keyof T extends never ? never : { [K in keyof T]: T[K] };
|
||||
>NotEmptyMapped : Symbol(NotEmptyMapped, Decl(typeParameterConstModifiers.ts, 91, 29))
|
||||
>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 93, 20))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 93, 20))
|
||||
>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 93, 88))
|
||||
>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 93, 20))
|
||||
>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 93, 20))
|
||||
>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 93, 88))
|
||||
|
||||
const thingMapped = <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => o;
|
||||
>thingMapped : Symbol(thingMapped, Decl(typeParameterConstModifiers.ts, 95, 5))
|
||||
>O : Symbol(O, Decl(typeParameterConstModifiers.ts, 95, 21))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>o : Symbol(o, Decl(typeParameterConstModifiers.ts, 95, 58))
|
||||
>NotEmptyMapped : Symbol(NotEmptyMapped, Decl(typeParameterConstModifiers.ts, 91, 29))
|
||||
>O : Symbol(O, Decl(typeParameterConstModifiers.ts, 95, 21))
|
||||
>o : Symbol(o, Decl(typeParameterConstModifiers.ts, 95, 58))
|
||||
|
||||
const tMapped = thingMapped({ foo: '' }); // { foo: "" }
|
||||
>tMapped : Symbol(tMapped, Decl(typeParameterConstModifiers.ts, 97, 5))
|
||||
>thingMapped : Symbol(thingMapped, Decl(typeParameterConstModifiers.ts, 95, 5))
|
||||
>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 97, 29))
|
||||
|
||||
|
||||
@ -350,3 +350,39 @@ const test2 = inners2([1,2,3,4,5]);
|
||||
>4 : 4
|
||||
>5 : 5
|
||||
|
||||
// Repro from #53307
|
||||
|
||||
type NotEmpty<T extends Record<string, any>> = keyof T extends never ? never : T;
|
||||
>NotEmpty : NotEmpty<T>
|
||||
|
||||
const thing = <const O extends Record<string, any>>(o: NotEmpty<O>) => o;
|
||||
>thing : <const O extends Record<string, any>>(o: NotEmpty<O>) => NotEmpty<O>
|
||||
><const O extends Record<string, any>>(o: NotEmpty<O>) => o : <const O extends Record<string, any>>(o: NotEmpty<O>) => NotEmpty<O>
|
||||
>o : NotEmpty<O>
|
||||
>o : NotEmpty<O>
|
||||
|
||||
const t = thing({ foo: '' }); // readonly { foo: "" }
|
||||
>t : { readonly foo: ""; }
|
||||
>thing({ foo: '' }) : { readonly foo: ""; }
|
||||
>thing : <const O extends Record<string, any>>(o: NotEmpty<O>) => NotEmpty<O>
|
||||
>{ foo: '' } : { foo: ""; }
|
||||
>foo : ""
|
||||
>'' : ""
|
||||
|
||||
type NotEmptyMapped<T extends Record<string, any>> = keyof T extends never ? never : { [K in keyof T]: T[K] };
|
||||
>NotEmptyMapped : NotEmptyMapped<T>
|
||||
|
||||
const thingMapped = <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => o;
|
||||
>thingMapped : <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => NotEmptyMapped<O>
|
||||
><const O extends Record<string, any>>(o: NotEmptyMapped<O>) => o : <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => NotEmptyMapped<O>
|
||||
>o : NotEmptyMapped<O>
|
||||
>o : NotEmptyMapped<O>
|
||||
|
||||
const tMapped = thingMapped({ foo: '' }); // { foo: "" }
|
||||
>tMapped : { foo: ""; }
|
||||
>thingMapped({ foo: '' }) : { foo: ""; }
|
||||
>thingMapped : <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => NotEmptyMapped<O>
|
||||
>{ foo: '' } : { foo: ""; }
|
||||
>foo : ""
|
||||
>'' : ""
|
||||
|
||||
|
||||
@ -84,3 +84,17 @@ const test = inners(1,2,3,4,5);
|
||||
declare function inners2<const T extends readonly any[]>(args: readonly [unknown, ...T, unknown]): T;
|
||||
|
||||
const test2 = inners2([1,2,3,4,5]);
|
||||
|
||||
// Repro from #53307
|
||||
|
||||
type NotEmpty<T extends Record<string, any>> = keyof T extends never ? never : T;
|
||||
|
||||
const thing = <const O extends Record<string, any>>(o: NotEmpty<O>) => o;
|
||||
|
||||
const t = thing({ foo: '' }); // readonly { foo: "" }
|
||||
|
||||
type NotEmptyMapped<T extends Record<string, any>> = keyof T extends never ? never : { [K in keyof T]: T[K] };
|
||||
|
||||
const thingMapped = <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => o;
|
||||
|
||||
const tMapped = thingMapped({ foo: '' }); // { foo: "" }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user