add restriction on narrowable type constraint

This commit is contained in:
Gabriela Araujo Britto
2025-02-01 12:02:52 -08:00
parent 739d729ecc
commit e0d466dcca
2 changed files with 28 additions and 4 deletions
+21 -3
View File
@@ -45976,14 +45976,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
/**
* Narrowable type parameters are type parameters that:
* (1) have a union type constraint;
* (2) are used as the type of a single parameter in the function, and nothing else
* (1) have a narrowable constraint;
* (2) are syntactically used as the type of a single parameter in the function, and nothing else
*/
function getNarrowableTypeParameters(candidates: TypeParameter[]): [TypeParameter, Symbol, Identifier][] {
const narrowableParams: [TypeParameter, Symbol, Identifier][] = [];
for (const typeParam of candidates) {
const constraint = getConstraintOfTypeParameter(typeParam);
if (!constraint || !(constraint.flags & TypeFlags.Union)) continue;
if (!constraint || !isNarrowableTypeParameterConstraint(constraint)) continue;
if (typeParam.symbol && typeParam.symbol.declarations && typeParam.symbol.declarations.length === 1) {
const declaration = typeParam.symbol.declarations[0];
const container = isJSDocTemplateTag(declaration.parent) ? getJSDocHost(declaration.parent) : declaration.parent;
@@ -46050,6 +46050,24 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
/**
* Determines if the type parameter constraint allows for narrowing of that type parameter.
* This is true if:
* (1) the constraint is a union type;
* (2) there's at most one non-primitive type in the union.
*/
function isNarrowableTypeParameterConstraint(constraint: Type): boolean {
if (!(constraint.flags & TypeFlags.Union)) return false;
let nonPrimitives = 0;
for (const type of (constraint as UnionType).types) {
if (!(type.flags & TypeFlags.Primitive)) {
nonPrimitives += 1;
}
}
return nonPrimitives <= 1;
}
function isNarrowableReturnType(returnType: IndexedAccessType | ConditionalType): boolean {
return isConditionalType(returnType)
? isNarrowableConditionalType(returnType)
@@ -22,6 +22,8 @@ dependentReturnType1.ts(206,28): error TS2322: Type 'number' is not assignable t
dependentReturnType1.ts(243,9): error TS2322: Type '""' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
dependentReturnType1.ts(245,9): error TS2322: Type 'true' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
dependentReturnType1.ts(247,5): error TS2322: Type '3' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
dependentReturnType1.ts(258,9): error TS2322: Type 'string' is not assignable to type 'T extends { a: string; } ? number : T extends { b: number; } ? string : never'.
dependentReturnType1.ts(261,5): error TS2322: Type 'number' is not assignable to type 'T extends { a: string; } ? number : T extends { b: number; } ? string : never'.
dependentReturnType1.ts(275,9): error TS2322: Type '1' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
dependentReturnType1.ts(278,9): error TS2322: Type '2' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
dependentReturnType1.ts(280,5): error TS2322: Type '0' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
@@ -44,7 +46,7 @@ dependentReturnType1.ts(488,9): error TS2322: Type 'R' is not assignable to type
dependentReturnType1.ts(514,5): error TS2322: Type '1' is not assignable to type 'never'.
==== dependentReturnType1.ts (39 errors) ====
==== dependentReturnType1.ts (41 errors) ====
interface A {
1: number;
2: string;
@@ -346,9 +348,13 @@ dependentReturnType1.ts(514,5): error TS2322: Type '1' is not assignable to type
if (q(x)) {
x.b;
return "";
~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'T extends { a: string; } ? number : T extends { b: number; } ? string : never'.
}
x.a;
return 1;
~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'T extends { a: string; } ? number : T extends { b: number; } ? string : never'.
}
let y = { a: "", b: 1 }