@@ -381,6 +381,7 @@ import {
381381 HasIllegalModifiers,
382382 HasInitializer,
383383 hasInitializer,
384+ HasInstanceMethodType,
384385 hasJSDocNodes,
385386 hasJSDocParameterTags,
386387 hasJsonModuleEmitEnabled,
@@ -2186,6 +2187,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
21862187 var potentialReflectCollisions: Node[] = [];
21872188 var potentialUnusedRenamedBindingElementsInTypes: BindingElement[] = [];
21882189 var awaitedTypeStack: number[] = [];
2190+ var hasGlobalSymbolHasInstanceProperty: boolean | undefined;
21892191
21902192 var diagnostics = createDiagnosticCollection();
21912193 var suggestionDiagnostics = createDiagnosticCollection();
@@ -27467,9 +27469,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2746727469 // if rightType is an object type with a custom `[Symbol.hasInstance]` method, and that method has a type
2746827470 // predicate, use the type predicate to perform narrowing. This allows normal `object` types to participate
2746927471 // in `instanceof`, as per Step 2 of https://tc39.es/ecma262/#sec-instanceofoperator.
27470- const customHasInstanceMethodType = getCustomSymbolHasInstanceMethodOfObjectType (rightType);
27471- if (customHasInstanceMethodType ) {
27472- const syntheticCall = createSyntheticHasInstanceMethodCall(left, expr.right, type, customHasInstanceMethodType );
27472+ const hasInstanceMethodType = getSymbolHasInstanceMethodOfObjectType (rightType);
27473+ if (hasInstanceMethodType ) {
27474+ const syntheticCall = createSyntheticHasInstanceMethodCall(left, expr.right, type, hasInstanceMethodType );
2747327475 const signature = getEffectsSignature(syntheticCall);
2747427476 const predicate = signature && getTypePredicateOfSignature(signature);
2747527477 if (predicate && (predicate.kind === TypePredicateKind.This || predicate.kind === TypePredicateKind.Identifier)) {
@@ -36475,13 +36477,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3647536477 }
3647636478
3647736479 /**
36478- * Get the type of the `[Symbol.hasInstance]` method of an object type, but only if it is not the
36479- * `[Symbol.hasInstance]` method inherited from the global `Function` type.
36480+ * Get the type of the `[Symbol.hasInstance]` method of an object type.
3648036481 */
36481- function getCustomSymbolHasInstanceMethodOfObjectType (type: Type) {
36482+ function getSymbolHasInstanceMethodOfObjectType (type: Type) {
3648236483 const hasInstancePropertyName = getPropertyNameForKnownSymbolName("hasInstance");
3648336484 const hasInstanceProperty = getPropertyOfObjectType(type, hasInstancePropertyName);
36484- if (hasInstanceProperty && hasInstanceProperty !== getPropertyOfObjectType(globalFunctionType, hasInstancePropertyName) ) {
36485+ if (hasInstanceProperty) {
3648536486 const hasInstancePropertyType = getTypeOfSymbol(hasInstanceProperty);
3648636487 if (hasInstancePropertyType && getSignaturesOfType(hasInstancePropertyType, SignatureKind.Call).length !== 0) {
3648736488 return hasInstancePropertyType;
@@ -36537,24 +36538,43 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3653736538 // if rightType is an object type with a custom `[Symbol.hasInstance]` method, then it is potentially
3653836539 // valid on the right-hand side of the `instanceof` operator. This allows normal `object` types to
3653936540 // participate in `instanceof`, as per Step 2 of https://tc39.es/ecma262/#sec-instanceofoperator.
36540- const customHasInstanceMethodType = getCustomSymbolHasInstanceMethodOfObjectType(rightType);
36541- if (customHasInstanceMethodType) {
36542- // If rightType has a `[Symbol.hasInstance]` method that is not the default [Symbol.hasInstance]() method on `Function`, check
36543- // that left is assignable to the first parameter.
36544- const syntheticCall = createSyntheticHasInstanceMethodCall(left, right, leftType, customHasInstanceMethodType);
36545- const returnType = getReturnTypeOfSignature(getResolvedSignature(syntheticCall));
36546-
36547- // Also verify that the return type of the `[Symbol.hasInstance]` method is assignable to `boolean`. The spec
36548- // will perform `ToBoolean` on the result, but this is more type-safe.
36549- checkTypeAssignableTo(returnType, booleanType, right, Diagnostics.An_object_s_Symbol_hasInstance_method_must_return_a_boolean_value_for_it_to_be_used_on_the_right_hand_side_of_an_instanceof_expression);
36541+ const hasInstanceMethodType = getSymbolHasInstanceMethodOfObjectType(rightType);
36542+ if (hasInstanceMethodType) {
36543+ // avoid a complex check for every `instanceof` when the `[Symbol.hasInstance]` method has a single
36544+ // call signature that neither restricts nor narrows (via type predicate) the LHS value, e.g.
36545+ // `(value: unknown) => boolean`.
36546+ const cache = hasInstanceMethodType as HasInstanceMethodType;
36547+ if (cache.hasSimpleUnrestrictedSingleCallSignature === undefined) {
36548+ const signature = getSingleCallSignature(hasInstanceMethodType);
36549+ cache.hasSimpleUnrestrictedSingleCallSignature = !!signature && signature.parameters.length === 1 &&
36550+ !!(getTypeOfSymbol(signature.parameters[0]).flags & TypeFlags.AnyOrUnknown) &&
36551+ !!signature.resolvedReturnType &&
36552+ !!(signature.resolvedReturnType.flags & TypeFlags.Boolean);
36553+ }
36554+ if (!cache.hasSimpleUnrestrictedSingleCallSignature) {
36555+ // If rightType has a `[Symbol.hasInstance]` method that is not `(value: unknown) => boolean`, we
36556+ // must check the expression as if it were a call to `right[Symbol.hasInstance](left)1. The call to
36557+ // `getResolvedSignature`, below, will check that leftType is assignable to the type of the first
36558+ // parameter.
36559+ const syntheticCall = createSyntheticHasInstanceMethodCall(left, right, leftType, hasInstanceMethodType);
36560+ const returnType = getReturnTypeOfSignature(getResolvedSignature(syntheticCall));
36561+
36562+ // We also verify that the return type of the `[Symbol.hasInstance]` method is assignable to
36563+ // `boolean`. According to the spec, the runtime will actually perform `ToBoolean` on the result,
36564+ // but this is more type-safe.
36565+ checkTypeAssignableTo(returnType, booleanType, right, Diagnostics.An_object_s_Symbol_hasInstance_method_must_return_a_boolean_value_for_it_to_be_used_on_the_right_hand_side_of_an_instanceof_expression);
36566+ }
3655036567 }
3655136568 // NOTE: do not raise error if right is unknown as related error was already reported
36552- if (!(customHasInstanceMethodType || typeHasCallOrConstructSignatures(rightType) || isTypeSubtypeOf(rightType, globalFunctionType))) {
36569+ if (!(hasInstanceMethodType || typeHasCallOrConstructSignatures(rightType) || isTypeSubtypeOf(rightType, globalFunctionType))) {
3655336570 // Do not indicate that `[Symbol.hasInstance]` is a valid option if it's not known to be present on `SymbolConstructor`.
36554- const globalESSymbolConstructorSymbol = getGlobalESSymbolConstructorTypeSymbol(/*reportErrors*/ false);
36555- const hasInstanceProp = globalESSymbolConstructorSymbol && getMembersOfSymbol(globalESSymbolConstructorSymbol).get(getPropertyNameForKnownSymbolName("hasInstance"));
36556- const message = hasInstanceProp ?
36557- Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_either_of_type_any_of_an_object_type_with_a_Symbol_hasInstance_method_or_of_a_type_assignable_to_the_Function_interface_type :
36571+ if (hasGlobalSymbolHasInstanceProperty === undefined) {
36572+ const globalESSymbolConstructorSymbol = getGlobalESSymbolConstructorTypeSymbol(/*reportErrors*/ false);
36573+ hasGlobalSymbolHasInstanceProperty = !!globalESSymbolConstructorSymbol && getMembersOfSymbol(globalESSymbolConstructorSymbol).has("hasInstance" as __String);
36574+ }
36575+
36576+ const message = hasGlobalSymbolHasInstanceProperty ?
36577+ Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_either_of_type_any_an_object_type_with_a_Symbol_hasInstance_method_or_a_type_assignable_to_the_Function_interface_type :
3655836578 Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type;
3655936579 error(right, message);
3656036580 }
0 commit comments