diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ec544ea0950d0..53ef034e6af9b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -528,6 +528,7 @@ namespace ts { let deferredGlobalExcludeSymbol: Symbol; let deferredGlobalPickSymbol: Symbol; let deferredGlobalBigIntType: ObjectType; + let deferredGlobalIfTypeSymbol: Symbol; const allPotentiallyUnusedIdentifiers = createMap(); // key is file name @@ -3993,7 +3994,8 @@ namespace ts { const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); context.flags = savedContextFlags; - return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); + const uniformityConstraint = getUniformityConstraintDeclaration(type); + return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode, uniformityConstraint); } function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext, constraint = getConstraintOfTypeParameter(type)): TypeParameterDeclaration { @@ -8525,6 +8527,11 @@ namespace ts { return decl && getEffectiveConstraintOfTypeParameter(decl); } + function getUniformityConstraintDeclaration(type: TypeParameter) { + const decl = type.symbol && getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter); + return decl && decl.uniformityConstraint ? decl.uniformityConstraint : 0; + } + function getInferredTypeParameterConstraint(typeParameter: TypeParameter) { let inferences: Type[] | undefined; if (typeParameter.symbol) { @@ -8585,6 +8592,13 @@ namespace ts { return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint; } + function getUniformityConstraintFromTypeParameter(typeParameter: TypeParameter): UniformityFlags { + if (typeParameter.uniformityConstraint === undefined) { + typeParameter.uniformityConstraint = getUniformityConstraintDeclaration(typeParameter); + } + return typeParameter.uniformityConstraint; + } + function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined { const tp = getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter)!; const host = isJSDocTemplateTag(tp.parent) ? getHostSignatureFromJSDoc(tp.parent) : tp.parent; @@ -9108,6 +9122,10 @@ namespace ts { return deferredGlobalPickSymbol || (deferredGlobalPickSymbol = getGlobalSymbol("Pick" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!); // TODO: GH#18217 } + function getGlobalIfTypeSymbol(): Symbol { + return deferredGlobalIfTypeSymbol || (deferredGlobalIfTypeSymbol = getGlobalSymbol("If" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!); // TODO: GH#18217 + } + function getGlobalBigIntType(reportErrors: boolean) { return deferredGlobalBigIntType || (deferredGlobalBigIntType = getGlobalType("BigInt" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; } @@ -10177,9 +10195,18 @@ namespace ts { // types with type parameters mapped to the wildcard type, the most permissive instantiations // possible (the wildcard type is assignable to and from all types). If those are not related, // then no instantiations will be and we can just return the false branch type. - if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { - return falseType; + if (isTypeNarrowableUnderUniformity(checkType, UniformityFlags.Equality | UniformityFlags.TypeOf)) { + if (!isTypeAssignableTo(getUniformInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { + return falseType; + } } + else { + if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { + return falseType; + } + } + + // Return trueType for a definitely true extends check. We check instantiations of the two // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter // that has no constraint. This ensures that, for example, the type @@ -10812,6 +10839,10 @@ namespace ts { return type.flags & TypeFlags.TypeParameter ? wildcardType : type; } + function unknownMapper(type: Type) { + return type.flags & TypeFlags.TypeParameter ? unknownType : type; + } + function getRestrictiveTypeParameter(tp: TypeParameter) { return tp.constraint === unknownType ? tp : tp.restrictiveInstantiation || ( tp.restrictiveInstantiation = createTypeParameter(tp.symbol), @@ -11190,6 +11221,11 @@ namespace ts { type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, permissiveMapper)); } + function getUniformInstantiation(type: Type) { + return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type : + type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, unknownMapper)); + } + function getRestrictiveInstantiation(type: Type) { if (type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never)) { return type; @@ -13909,6 +13945,16 @@ namespace ts { return value.base10Value === "0"; } + function isTypeNarrowableUnderUniformity(type: Type, kind: UniformityFlags): boolean { + if (type.flags & TypeFlags.TypeParameter) { + return (getUniformityConstraintFromTypeParameter(type) & kind) !== 0; + } + if (type.flags & TypeFlags.Intersection) { + return (type).types.some(t => isTypeNarrowableUnderUniformity(t, kind)); + } + return false; + } + function getFalsyFlagsOfTypes(types: Type[]): TypeFlags { let result: TypeFlags = 0; for (const t of types) { @@ -15043,6 +15089,12 @@ namespace ts { inference.inferredType = inferredType; const constraint = getConstraintOfTypeParameter(inference.typeParameter); + const uniformityConstraint = getUniformityConstraintFromTypeParameter(inference.typeParameter); + if (uniformityConstraint) { + if (!isUniformType(inferredType, uniformityConstraint)) { + error(/*location*/ undefined, Diagnostics.Type_0_does_not_satisfy_uniformity_constraint_of_type_1_Values_of_type_0_do_not_behave_identically_under_typeof, typeToString(inferredType), typeToString(inference.typeParameter)); + } + } if (constraint) { context.flags |= InferenceFlags.NoFixing; const instantiatedConstraint = instantiateType(constraint, context); @@ -16247,7 +16299,8 @@ namespace ts { if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) { return declaredType; } - break; + return narrowTypeByUniformEquality(type, operator, left, right, assumeTrue); + case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); case SyntaxKind.InKeyword: @@ -16262,6 +16315,45 @@ namespace ts { return type; } + function narrowTypeByUniformEquality(type: Type, operator: SyntaxKind, left: Expression, right: Expression, assumeTrue: boolean): Type { + if ((operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) { + assumeTrue = !assumeTrue; + } + if (!assumeTrue || operator === SyntaxKind.EqualsEqualsToken) { + return type; + } + const leftType = getTypeOfExpression(left); + const rightType = getTypeOfExpression(right); + const leftIsNarrowable = isTypeNarrowableUnderUniformity(leftType, UniformityFlags.Equality | UniformityFlags.TypeOf); + if (leftIsNarrowable && isUnitType(rightType)) { + if (leftType === type) { + return getIntersectionType([type, rightType]); + } + if (type.flags & TypeFlags.Conditional) { + const cond = type; + const subst = getIntersectionType([leftType, rightType]); + const mapper = createTypeMapper([leftType], [subst]); + const narrowedMapper = combineTypeMappers(cond.mapper, mapper); + return instantiateType(cond, narrowedMapper); + } + return type; + } + const rightIsNarrowable = isTypeNarrowableUnderUniformity(rightType, UniformityFlags.Equality | UniformityFlags.TypeOf); + if (rightIsNarrowable && isUnitType(leftType)) { + if (rightType === type) { + return getIntersectionType([type, leftType]); + } + if (type.flags & TypeFlags.Conditional) { + const cond = type; + const subst = getIntersectionType([rightType, leftType]); + const mapper = createTypeMapper([rightType], [subst]); + const narrowedMapper = combineTypeMappers(cond.mapper, mapper); + return instantiateType(cond, narrowedMapper); + } + } + return type; + } + function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { if (type.flags & TypeFlags.Any) { return type; @@ -16314,7 +16406,34 @@ namespace ts { if (containsMatchingReference(reference, target)) { return declaredType; } - return type; + if (!isIdentifier(target)) { + return type; + } + const targetType = getTypeOfExpression(target); + const isNarrowableUnderUniformity = isTypeNarrowableUnderUniformity(targetType, UniformityFlags.TypeOf); + if (!isNarrowableUnderUniformity) { + return type; + } + if (type.flags & TypeFlags.Conditional) { + if ((operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) { + assumeTrue = !assumeTrue; + } + if (!assumeTrue || operator === SyntaxKind.EqualsEqualsToken) { + return type; + } + const cond = type; + const substType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text); + if (!substType) { + return type; + } + const subst = getIntersectionType([targetType, substType]); + const mapper = createTypeMapper([targetType], [subst]); + const narrowedMapper = combineTypeMappers(cond.mapper, mapper); + return instantiateType(cond, narrowedMapper); + } + if (targetType !== type) { + return declaredType; + } } if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; @@ -16348,6 +16467,10 @@ namespace ts { return getIntersectionType([type, targetType]); } } + if (isTypeNarrowableUnderUniformity(type, UniformityFlags.TypeOf)) { + const narrowed = getIntersectionType([type, targetType]); + return (narrowed.flags & TypeFlags.Intersection) && isEmptyIntersectionType(narrowed) ? neverType : narrowed; + } } return type; } @@ -20270,6 +20393,22 @@ namespace ts { createTupleType(append(types.slice(0, spreadIndex), getUnionType(types.slice(spreadIndex))), spreadIndex, /*hasRestElement*/ true); } + function isUniformType(type: Type, kind: UniformityFlags): boolean { + if (kind & UniformityFlags.Equality) { + return isUnitType(type); + } + else if (kind & UniformityFlags.TypeOf) { + // jw todo: more cases + if (isUnitType(type) || (type.flags & TypeFlags.Primitive)) { + return true; + } + if (type.flags & TypeFlags.TypeParameter) { + return !!getUniformityConstraintFromTypeParameter(type); + } + } + return false; + } + function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray, reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined { const isJavascript = isInJSFile(signature.declaration); const typeParameters = signature.typeParameters!; @@ -20278,6 +20417,12 @@ namespace ts { for (let i = 0; i < typeArgumentNodes.length; i++) { Debug.assert(typeParameters[i] !== undefined, "Should not call checkTypeArguments with too many type arguments"); const constraint = getConstraintOfTypeParameter(typeParameters[i]); + const uniformityConstraint = getUniformityConstraintFromTypeParameter(typeParameters[i]); + if (uniformityConstraint) { + if (!isUniformType(typeArgumentTypes[i], uniformityConstraint)) { + error(typeArgumentNodes[i], Diagnostics.Type_0_does_not_satisfy_uniformity_constraint_of_type_1_Values_of_type_0_do_not_behave_identically_under_typeof, typeToString(typeArgumentTypes[i]), typeToString(typeParameters[i])); + } + } if (constraint) { const errorInfo = reportErrors && headMessage ? (() => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1)) : undefined; const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1; @@ -23300,6 +23445,19 @@ namespace ts { checkTruthinessExpression(node.condition); const type1 = checkExpression(node.whenTrue, checkMode); const type2 = checkExpression(node.whenFalse, checkMode); + if (isBinaryExpression(node.condition) && node.condition.left.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(node.condition.right)) { + const extendsType = typeofTypesByName.get(node.condition.right.text); + if (extendsType !== undefined) { + const typeofTest = getTypeOfExpression((node.condition.left).expression); + if ((typeofTest.flags & TypeFlags.TypeVariable) && getUniformityConstraintFromTypeParameter(typeofTest)) { + const ifTypeAlias = getGlobalIfTypeSymbol(); + if (!ifTypeAlias) { + return errorType; + } + return getTypeAliasInstantiation(ifTypeAlias, [typeofTest, extendsType, type1, type2]); + } + } + } return getUnionType([type1, type2], UnionReduction.Subtype); } @@ -24353,6 +24511,16 @@ namespace ts { let result = true; for (let i = 0; i < typeParameters.length; i++) { const constraint = getConstraintOfTypeParameter(typeParameters[i]); + const uniformityConstraint = getUniformityConstraintFromTypeParameter(typeParameters[i]); + if (uniformityConstraint) { + if (!typeArguments) { + typeArguments = getEffectiveTypeArguments(node, typeParameters); + mapper = createTypeMapper(typeParameters, typeArguments); + } + if (!isUniformType(typeArguments[i], uniformityConstraint)) { + error(node.typeArguments && node.typeArguments[i], Diagnostics.Type_0_does_not_satisfy_uniformity_constraint_of_type_1_Values_of_type_0_do_not_behave_identically_under_typeof, typeToString(typeArguments[i]), typeToString(typeParameters[i])); + } + } if (constraint) { if (!typeArguments) { typeArguments = getEffectiveTypeArguments(node, typeParameters); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 53094bf188852..06bbbf43c130b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2593,7 +2593,10 @@ "category": "Error", "code": 2751 }, - + "Type '{0}' does not satisfy uniformity constraint of type '{1}'. Values of type '{0}' do not behave identically under typeof": { + "category": "Error", + "code": 2752 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e55e768bab31f..a2d87a7bfd3fb 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1637,6 +1637,12 @@ namespace ts { function emitTypeParameter(node: TypeParameterDeclaration) { emit(node.name); + if (node.uniformityConstraint && node.uniformityConstraint & UniformityFlags.TypeOf) { + writePunctuation("!"); + } + else if (node.uniformityConstraint && node.uniformityConstraint & UniformityFlags.Equality) { + writePunctuation("~"); + } if (node.constraint) { writeSpace(); writeKeyword("extends"); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index a3bffb785a8fc..ded8151624151 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -300,19 +300,21 @@ namespace ts { // Signature elements - export function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode) { + export function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode, uniformityConstraint?: UniformityFlags) { const node = createSynthesizedNode(SyntaxKind.TypeParameter) as TypeParameterDeclaration; node.name = asName(name); node.constraint = constraint; node.default = defaultType; + node.uniformityConstraint = uniformityConstraint; return node; } - export function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined) { + export function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined, uniformityConstraint?: UniformityFlags) { return node.name !== name || node.constraint !== constraint || node.default !== defaultType - ? updateNode(createTypeParameterDeclaration(name, constraint, defaultType), node) + || node.uniformityConstraint !== uniformityConstraint + ? updateNode(createTypeParameterDeclaration(name, constraint, defaultType, uniformityConstraint), node) : node; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1564b78ca61d2..73fd0d89a8411 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3,7 +3,7 @@ namespace ts { None = 0, Yield = 1 << 0, Await = 1 << 1, - Type = 1 << 2, + Type = 1 << 2, IgnoreMissingOpenBrace = 1 << 4, JSDoc = 1 << 5, } @@ -2415,6 +2415,9 @@ namespace ts { function parseTypeParameter(): TypeParameterDeclaration { const node = createNode(SyntaxKind.TypeParameter); node.name = parseIdentifier(); + node.uniformityConstraint = 0; + node.uniformityConstraint |= parseOptional(SyntaxKind.ExclamationToken) ? UniformityFlags.TypeOf : 0; + node.uniformityConstraint |= parseOptional(SyntaxKind.TildeToken) ? UniformityFlags.Equality : 0; if (parseOptional(SyntaxKind.ExtendsKeyword)) { // It's not uncommon for people to write improper constraints to a generic. If the // user writes a constraint that is an expression and not an actual type, then parse diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 1f88bfb9e4f34..ee1d93866efe5 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -847,7 +847,7 @@ namespace ts { } case SyntaxKind.TypeParameter: { if (isPrivateMethodTypeParameter(input) && (input.default || input.constraint)) { - return cleanup(updateTypeParameterDeclaration(input, input.name, /*constraint*/ undefined, /*defaultType*/ undefined)); + return cleanup(updateTypeParameterDeclaration(input, input.name, /*constraint*/ undefined, /*defaultType*/ undefined, /*uniformityConstraint*/ undefined)); } return cleanup(visitEachChild(input, visitDeclarationSubtree, context)); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 20fab57c36b18..f6942b287dffb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -852,6 +852,8 @@ namespace ts { // For error recovery purposes. expression?: Expression; + + uniformityConstraint?: UniformityFlags; } export interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer { @@ -4041,6 +4043,11 @@ namespace ts { export interface EnumType extends Type { } + export const enum UniformityFlags { + TypeOf = 1 << 0, + Equality = 1 << 1, + } + export const enum ObjectFlags { Class = 1 << 0, // Class Interface = 1 << 1, // Interface @@ -4272,6 +4279,8 @@ namespace ts { isThisType?: boolean; /* @internal */ resolvedDefaultType?: Type; + /* @internal */ + uniformityConstraint?: UniformityFlags; } // Indexed access types (TypeFlags.IndexedAccess) diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 0d4d0d9482d6a..91bb2777dc59e 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -237,7 +237,8 @@ namespace ts { return updateTypeParameterDeclaration(node, visitNode((node).name, visitor, isIdentifier), visitNode((node).constraint, visitor, isTypeNode), - visitNode((node).default, visitor, isTypeNode)); + visitNode((node).default, visitor, isTypeNode), + (node).uniformityConstraint); case SyntaxKind.Parameter: return updateParameter(node, diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 16f1a37ae83fa..1d7452b8c140f 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -4504,6 +4504,7 @@ namespace FourSlashInterface { typeEntry("Required"), typeEntry("Readonly"), typeEntry("Pick"), + typeEntry("If"), typeEntry("Record"), typeEntry("Exclude"), typeEntry("Extract"), diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 4112f5a2ae7d7..489c55d7e9dc5 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1426,6 +1426,8 @@ type Pick = { [P in K]: T[P]; }; +type If = [C] extends [E] ? T : F; + /** * Construct a type with a set of properties K of type T */ diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e99261be3f3b5..04e8cc2db8080 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -563,6 +563,7 @@ declare namespace ts { constraint?: TypeNode; default?: TypeNode; expression?: Expression; + uniformityConstraint?: UniformityFlags; } interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer { kind: SignatureDeclaration["kind"]; @@ -2277,6 +2278,10 @@ declare namespace ts { } interface EnumType extends Type { } + enum UniformityFlags { + TypeOf = 1, + Equality = 2 + } enum ObjectFlags { Class = 1, Interface = 2, @@ -3757,8 +3762,8 @@ declare namespace ts { function updateQualifiedName(node: QualifiedName, left: EntityName, right: Identifier): QualifiedName; function createComputedPropertyName(expression: Expression): ComputedPropertyName; function updateComputedPropertyName(node: ComputedPropertyName, expression: Expression): ComputedPropertyName; - function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode): TypeParameterDeclaration; - function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined): TypeParameterDeclaration; + function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode, uniformityConstraint?: UniformityFlags): TypeParameterDeclaration; + function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined, uniformityConstraint?: UniformityFlags): TypeParameterDeclaration; function createParameter(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration; function updateParameter(node: ParameterDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): ParameterDeclaration; function createDecorator(expression: Expression): Decorator; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index d7b494960537c..75a519b37f972 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -563,6 +563,7 @@ declare namespace ts { constraint?: TypeNode; default?: TypeNode; expression?: Expression; + uniformityConstraint?: UniformityFlags; } interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer { kind: SignatureDeclaration["kind"]; @@ -2277,6 +2278,10 @@ declare namespace ts { } interface EnumType extends Type { } + enum UniformityFlags { + TypeOf = 1, + Equality = 2 + } enum ObjectFlags { Class = 1, Interface = 2, @@ -3757,8 +3762,8 @@ declare namespace ts { function updateQualifiedName(node: QualifiedName, left: EntityName, right: Identifier): QualifiedName; function createComputedPropertyName(expression: Expression): ComputedPropertyName; function updateComputedPropertyName(node: ComputedPropertyName, expression: Expression): ComputedPropertyName; - function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode): TypeParameterDeclaration; - function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined): TypeParameterDeclaration; + function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode, uniformityConstraint?: UniformityFlags): TypeParameterDeclaration; + function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined, uniformityConstraint?: UniformityFlags): TypeParameterDeclaration; function createParameter(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration; function updateParameter(node: ParameterDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): ParameterDeclaration; function createDecorator(expression: Expression): Decorator; diff --git a/tests/baselines/reference/conditionalTypes1.errors.txt b/tests/baselines/reference/conditionalTypes1.errors.txt index ec35f89161589..647c96af646ab 100644 --- a/tests/baselines/reference/conditionalTypes1.errors.txt +++ b/tests/baselines/reference/conditionalTypes1.errors.txt @@ -290,10 +290,10 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS type T38 = [T] extends [{ a: string }] ? [T] extends [{ b: number }] ? T35 : never : never; type Extends = T extends U ? true : false; - type If = C extends true ? T : F; - type Not = If; - type And = If; - type Or = If; + type IfC = C extends true ? T : F; + type Not = IfC; + type And = IfC; + type Or = IfC; type IsString = Extends; @@ -420,9 +420,9 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS function f50() { type Eq = T extends U ? U extends T ? true : false : false; - type If = S extends false ? U : T; - type Omit = { [P in keyof T]: If, never, P>; }[keyof T]; - type Omit2 = { [P in keyof T]: If, never, P>; }[keyof T]; + type IfC = S extends false ? U : T; + type Omit = { [P in keyof T]: IfC, never, P>; }[keyof T]; + type Omit2 = { [P in keyof T]: IfC, never, P>; }[keyof T]; type A = Omit<{ a: void; b: never; }>; // 'a' type B = Omit2<{ a: void; b: never; }>; // 'a' } diff --git a/tests/baselines/reference/conditionalTypes1.js b/tests/baselines/reference/conditionalTypes1.js index b592bd33d10e8..faed16a136ac5 100644 --- a/tests/baselines/reference/conditionalTypes1.js +++ b/tests/baselines/reference/conditionalTypes1.js @@ -167,10 +167,10 @@ type T37 = T extends { b: number } ? T extends { a: string } ? T35 : never type T38 = [T] extends [{ a: string }] ? [T] extends [{ b: number }] ? T35 : never : never; type Extends = T extends U ? true : false; -type If = C extends true ? T : F; -type Not = If; -type And = If; -type Or = If; +type IfC = C extends true ? T : F; +type Not = IfC; +type And = IfC; +type Or = IfC; type IsString = Extends; @@ -292,9 +292,9 @@ const f45 = (value: T95): T94 => value; // Error function f50() { type Eq = T extends U ? U extends T ? true : false : false; - type If = S extends false ? U : T; - type Omit = { [P in keyof T]: If, never, P>; }[keyof T]; - type Omit2 = { [P in keyof T]: If, never, P>; }[keyof T]; + type IfC = S extends false ? U : T; + type Omit = { [P in keyof T]: IfC, never, P>; }[keyof T]; + type Omit2 = { [P in keyof T]: IfC, never, P>; }[keyof T]; type A = Omit<{ a: void; b: never; }>; // 'a' type B = Omit2<{ a: void; b: never; }>; // 'a' } @@ -591,10 +591,10 @@ declare type T38 = [T] extends [{ b: number; }] ? T35 : never : never; declare type Extends = T extends U ? true : false; -declare type If = C extends true ? T : F; -declare type Not = If; -declare type And = If; -declare type Or = If; +declare type IfC = C extends true ? T : F; +declare type Not = IfC; +declare type And = IfC; +declare type Or = IfC; declare type IsString = Extends; declare type Q1 = IsString; declare type Q2 = IsString<"abc">; diff --git a/tests/baselines/reference/conditionalTypes1.symbols b/tests/baselines/reference/conditionalTypes1.symbols index e5c6ae3d6c7f7..e4c9d215a2f08 100644 --- a/tests/baselines/reference/conditionalTypes1.symbols +++ b/tests/baselines/reference/conditionalTypes1.symbols @@ -656,142 +656,142 @@ type Extends = T extends U ? true : false; >T : Symbol(T, Decl(conditionalTypes1.ts, 167, 13)) >U : Symbol(U, Decl(conditionalTypes1.ts, 167, 15)) -type If = C extends true ? T : F; ->If : Symbol(If, Decl(conditionalTypes1.ts, 167, 48)) ->C : Symbol(C, Decl(conditionalTypes1.ts, 168, 8)) ->T : Symbol(T, Decl(conditionalTypes1.ts, 168, 26)) ->F : Symbol(F, Decl(conditionalTypes1.ts, 168, 29)) ->C : Symbol(C, Decl(conditionalTypes1.ts, 168, 8)) ->T : Symbol(T, Decl(conditionalTypes1.ts, 168, 26)) ->F : Symbol(F, Decl(conditionalTypes1.ts, 168, 29)) - -type Not = If; ->Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 58)) +type IfC = C extends true ? T : F; +>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 167, 48)) +>C : Symbol(C, Decl(conditionalTypes1.ts, 168, 9)) +>T : Symbol(T, Decl(conditionalTypes1.ts, 168, 27)) +>F : Symbol(F, Decl(conditionalTypes1.ts, 168, 30)) +>C : Symbol(C, Decl(conditionalTypes1.ts, 168, 9)) +>T : Symbol(T, Decl(conditionalTypes1.ts, 168, 27)) +>F : Symbol(F, Decl(conditionalTypes1.ts, 168, 30)) + +type Not = IfC; +>Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 59)) >C : Symbol(C, Decl(conditionalTypes1.ts, 169, 9)) ->If : Symbol(If, Decl(conditionalTypes1.ts, 167, 48)) +>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 167, 48)) >C : Symbol(C, Decl(conditionalTypes1.ts, 169, 9)) -type And = If; ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +type And = IfC; +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) >A : Symbol(A, Decl(conditionalTypes1.ts, 170, 9)) >B : Symbol(B, Decl(conditionalTypes1.ts, 170, 27)) ->If : Symbol(If, Decl(conditionalTypes1.ts, 167, 48)) +>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 167, 48)) >A : Symbol(A, Decl(conditionalTypes1.ts, 170, 9)) >B : Symbol(B, Decl(conditionalTypes1.ts, 170, 27)) -type Or = If; ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +type Or = IfC; +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) >A : Symbol(A, Decl(conditionalTypes1.ts, 171, 8)) >B : Symbol(B, Decl(conditionalTypes1.ts, 171, 26)) ->If : Symbol(If, Decl(conditionalTypes1.ts, 167, 48)) +>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 167, 48)) >A : Symbol(A, Decl(conditionalTypes1.ts, 171, 8)) >B : Symbol(B, Decl(conditionalTypes1.ts, 171, 26)) type IsString = Extends; ->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63)) +>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64)) >T : Symbol(T, Decl(conditionalTypes1.ts, 173, 14)) >Extends : Symbol(Extends, Decl(conditionalTypes1.ts, 165, 97)) >T : Symbol(T, Decl(conditionalTypes1.ts, 173, 14)) type Q1 = IsString; // false >Q1 : Symbol(Q1, Decl(conditionalTypes1.ts, 173, 38)) ->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63)) +>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64)) type Q2 = IsString<"abc">; // true >Q2 : Symbol(Q2, Decl(conditionalTypes1.ts, 175, 27)) ->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63)) +>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64)) type Q3 = IsString; // boolean >Q3 : Symbol(Q3, Decl(conditionalTypes1.ts, 176, 26)) ->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63)) +>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64)) type Q4 = IsString; // never >Q4 : Symbol(Q4, Decl(conditionalTypes1.ts, 177, 24)) ->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63)) +>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64)) type N1 = Not; // true >N1 : Symbol(N1, Decl(conditionalTypes1.ts, 178, 26)) ->Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 58)) +>Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 59)) type N2 = Not; // false >N2 : Symbol(N2, Decl(conditionalTypes1.ts, 180, 21)) ->Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 58)) +>Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 59)) type N3 = Not; // boolean >N3 : Symbol(N3, Decl(conditionalTypes1.ts, 181, 20)) ->Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 58)) +>Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 59)) type A1 = And; // false >A1 : Symbol(A1, Decl(conditionalTypes1.ts, 182, 23)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type A2 = And; // false >A2 : Symbol(A2, Decl(conditionalTypes1.ts, 184, 28)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type A3 = And; // false >A3 : Symbol(A3, Decl(conditionalTypes1.ts, 185, 27)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type A4 = And; // true >A4 : Symbol(A4, Decl(conditionalTypes1.ts, 186, 27)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type A5 = And; // false >A5 : Symbol(A5, Decl(conditionalTypes1.ts, 187, 26)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type A6 = And; // false >A6 : Symbol(A6, Decl(conditionalTypes1.ts, 188, 30)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type A7 = And; // boolean >A7 : Symbol(A7, Decl(conditionalTypes1.ts, 189, 30)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type A8 = And; // boolean >A8 : Symbol(A8, Decl(conditionalTypes1.ts, 190, 29)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type A9 = And; // boolean >A9 : Symbol(A9, Decl(conditionalTypes1.ts, 191, 29)) ->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49)) +>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50)) type O1 = Or; // false >O1 : Symbol(O1, Decl(conditionalTypes1.ts, 192, 32)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type O2 = Or; // true >O2 : Symbol(O2, Decl(conditionalTypes1.ts, 194, 27)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type O3 = Or; // true >O3 : Symbol(O3, Decl(conditionalTypes1.ts, 195, 26)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type O4 = Or; // true >O4 : Symbol(O4, Decl(conditionalTypes1.ts, 196, 26)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type O5 = Or; // boolean >O5 : Symbol(O5, Decl(conditionalTypes1.ts, 197, 25)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type O6 = Or; // boolean >O6 : Symbol(O6, Decl(conditionalTypes1.ts, 198, 29)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type O7 = Or; // true >O7 : Symbol(O7, Decl(conditionalTypes1.ts, 199, 29)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type O8 = Or; // true >O8 : Symbol(O8, Decl(conditionalTypes1.ts, 200, 28)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type O9 = Or; // boolean >O9 : Symbol(O9, Decl(conditionalTypes1.ts, 201, 28)) ->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65)) +>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66)) type T40 = never extends never ? true : false; // true >T40 : Symbol(T40, Decl(conditionalTypes1.ts, 202, 31)) @@ -1136,34 +1136,34 @@ function f50() { >U : Symbol(U, Decl(conditionalTypes1.ts, 292, 14)) >T : Symbol(T, Decl(conditionalTypes1.ts, 292, 12)) - type If = S extends false ? U : T; ->If : Symbol(If, Decl(conditionalTypes1.ts, 292, 69)) ->S : Symbol(S, Decl(conditionalTypes1.ts, 293, 12)) ->T : Symbol(T, Decl(conditionalTypes1.ts, 293, 14)) ->U : Symbol(U, Decl(conditionalTypes1.ts, 293, 17)) ->S : Symbol(S, Decl(conditionalTypes1.ts, 293, 12)) ->U : Symbol(U, Decl(conditionalTypes1.ts, 293, 17)) ->T : Symbol(T, Decl(conditionalTypes1.ts, 293, 14)) - - type Omit = { [P in keyof T]: If, never, P>; }[keyof T]; ->Omit : Symbol(Omit, Decl(conditionalTypes1.ts, 293, 47)) + type IfC = S extends false ? U : T; +>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 292, 69)) +>S : Symbol(S, Decl(conditionalTypes1.ts, 293, 13)) +>T : Symbol(T, Decl(conditionalTypes1.ts, 293, 15)) +>U : Symbol(U, Decl(conditionalTypes1.ts, 293, 18)) +>S : Symbol(S, Decl(conditionalTypes1.ts, 293, 13)) +>U : Symbol(U, Decl(conditionalTypes1.ts, 293, 18)) +>T : Symbol(T, Decl(conditionalTypes1.ts, 293, 15)) + + type Omit = { [P in keyof T]: IfC, never, P>; }[keyof T]; +>Omit : Symbol(Omit, Decl(conditionalTypes1.ts, 293, 48)) >T : Symbol(T, Decl(conditionalTypes1.ts, 294, 14)) >P : Symbol(P, Decl(conditionalTypes1.ts, 294, 37)) >T : Symbol(T, Decl(conditionalTypes1.ts, 294, 14)) ->If : Symbol(If, Decl(conditionalTypes1.ts, 292, 69)) +>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 292, 69)) >Eq : Symbol(Eq, Decl(conditionalTypes1.ts, 291, 16)) >T : Symbol(T, Decl(conditionalTypes1.ts, 294, 14)) >P : Symbol(P, Decl(conditionalTypes1.ts, 294, 37)) >P : Symbol(P, Decl(conditionalTypes1.ts, 294, 37)) >T : Symbol(T, Decl(conditionalTypes1.ts, 294, 14)) - type Omit2 = { [P in keyof T]: If, never, P>; }[keyof T]; ->Omit2 : Symbol(Omit2, Decl(conditionalTypes1.ts, 294, 94)) + type Omit2 = { [P in keyof T]: IfC, never, P>; }[keyof T]; +>Omit2 : Symbol(Omit2, Decl(conditionalTypes1.ts, 294, 95)) >T : Symbol(T, Decl(conditionalTypes1.ts, 295, 15)) >U : Symbol(U, Decl(conditionalTypes1.ts, 295, 32)) >P : Symbol(P, Decl(conditionalTypes1.ts, 295, 49)) >T : Symbol(T, Decl(conditionalTypes1.ts, 295, 15)) ->If : Symbol(If, Decl(conditionalTypes1.ts, 292, 69)) +>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 292, 69)) >Eq : Symbol(Eq, Decl(conditionalTypes1.ts, 291, 16)) >T : Symbol(T, Decl(conditionalTypes1.ts, 295, 15)) >P : Symbol(P, Decl(conditionalTypes1.ts, 295, 49)) @@ -1172,14 +1172,14 @@ function f50() { >T : Symbol(T, Decl(conditionalTypes1.ts, 295, 15)) type A = Omit<{ a: void; b: never; }>; // 'a' ->A : Symbol(A, Decl(conditionalTypes1.ts, 295, 102)) ->Omit : Symbol(Omit, Decl(conditionalTypes1.ts, 293, 47)) +>A : Symbol(A, Decl(conditionalTypes1.ts, 295, 103)) +>Omit : Symbol(Omit, Decl(conditionalTypes1.ts, 293, 48)) >a : Symbol(a, Decl(conditionalTypes1.ts, 296, 19)) >b : Symbol(b, Decl(conditionalTypes1.ts, 296, 28)) type B = Omit2<{ a: void; b: never; }>; // 'a' >B : Symbol(B, Decl(conditionalTypes1.ts, 296, 42)) ->Omit2 : Symbol(Omit2, Decl(conditionalTypes1.ts, 294, 94)) +>Omit2 : Symbol(Omit2, Decl(conditionalTypes1.ts, 294, 95)) >a : Symbol(a, Decl(conditionalTypes1.ts, 297, 20)) >b : Symbol(b, Decl(conditionalTypes1.ts, 297, 29)) } diff --git a/tests/baselines/reference/conditionalTypes1.types b/tests/baselines/reference/conditionalTypes1.types index 8cca94f0a1021..3d3fbfaec4fbb 100644 --- a/tests/baselines/reference/conditionalTypes1.types +++ b/tests/baselines/reference/conditionalTypes1.types @@ -518,21 +518,21 @@ type Extends = T extends U ? true : false; >true : true >false : false -type If = C extends true ? T : F; ->If : If +type IfC = C extends true ? T : F; +>IfC : IfC >true : true -type Not = If; ->Not : If +type Not = IfC; +>Not : IfC >false : false >true : true -type And = If; ->And : If +type And = IfC; +>And : IfC >false : false -type Or = If; ->Or : If +type Or = IfC; +>Or : IfC >true : true type IsString = Extends; @@ -909,14 +909,14 @@ function f50() { >false : false >false : false - type If = S extends false ? U : T; ->If : S extends false ? U : T + type IfC = S extends false ? U : T; +>IfC : S extends false ? U : T >false : false - type Omit = { [P in keyof T]: If, never, P>; }[keyof T]; + type Omit = { [P in keyof T]: IfC, never, P>; }[keyof T]; >Omit : { [P in keyof T]: (T[P] extends never ? never : false) extends false ? P : never; }[keyof T] - type Omit2 = { [P in keyof T]: If, never, P>; }[keyof T]; + type Omit2 = { [P in keyof T]: IfC, never, P>; }[keyof T]; >Omit2 : { [P in keyof T]: (T[P] extends U ? U extends T[P] ? true : false : false) extends false ? P : never; }[keyof T] type A = Omit<{ a: void; b: never; }>; // 'a' diff --git a/tests/cases/conformance/types/conditional/conditionalTypes1.ts b/tests/cases/conformance/types/conditional/conditionalTypes1.ts index 2e8690a228b45..91ccfec356e3b 100644 --- a/tests/cases/conformance/types/conditional/conditionalTypes1.ts +++ b/tests/cases/conformance/types/conditional/conditionalTypes1.ts @@ -169,10 +169,10 @@ type T37 = T extends { b: number } ? T extends { a: string } ? T35 : never type T38 = [T] extends [{ a: string }] ? [T] extends [{ b: number }] ? T35 : never : never; type Extends = T extends U ? true : false; -type If = C extends true ? T : F; -type Not = If; -type And = If; -type Or = If; +type IfC = C extends true ? T : F; +type Not = IfC; +type And = IfC; +type Or = IfC; type IsString = Extends; @@ -294,9 +294,9 @@ const f45 = (value: T95): T94 => value; // Error function f50() { type Eq = T extends U ? U extends T ? true : false : false; - type If = S extends false ? U : T; - type Omit = { [P in keyof T]: If, never, P>; }[keyof T]; - type Omit2 = { [P in keyof T]: If, never, P>; }[keyof T]; + type IfC = S extends false ? U : T; + type Omit = { [P in keyof T]: IfC, never, P>; }[keyof T]; + type Omit2 = { [P in keyof T]: IfC, never, P>; }[keyof T]; type A = Omit<{ a: void; b: never; }>; // 'a' type B = Omit2<{ a: void; b: never; }>; // 'a' }