@@ -381,6 +381,7 @@ import {
381
381
HasIllegalModifiers,
382
382
HasInitializer,
383
383
hasInitializer,
384
+ HasInstanceMethodType,
384
385
hasJSDocNodes,
385
386
hasJSDocParameterTags,
386
387
hasJsonModuleEmitEnabled,
@@ -2186,6 +2187,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2186
2187
var potentialReflectCollisions: Node[] = [];
2187
2188
var potentialUnusedRenamedBindingElementsInTypes: BindingElement[] = [];
2188
2189
var awaitedTypeStack: number[] = [];
2190
+ var hasGlobalSymbolHasInstanceProperty: boolean | undefined;
2189
2191
2190
2192
var diagnostics = createDiagnosticCollection();
2191
2193
var suggestionDiagnostics = createDiagnosticCollection();
@@ -27467,9 +27469,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
27467
27469
// if rightType is an object type with a custom `[Symbol.hasInstance]` method, and that method has a type
27468
27470
// predicate, use the type predicate to perform narrowing. This allows normal `object` types to participate
27469
27471
// 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 );
27473
27475
const signature = getEffectsSignature(syntheticCall);
27474
27476
const predicate = signature && getTypePredicateOfSignature(signature);
27475
27477
if (predicate && (predicate.kind === TypePredicateKind.This || predicate.kind === TypePredicateKind.Identifier)) {
@@ -36475,13 +36477,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
36475
36477
}
36476
36478
36477
36479
/**
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.
36480
36481
*/
36481
- function getCustomSymbolHasInstanceMethodOfObjectType (type: Type) {
36482
+ function getSymbolHasInstanceMethodOfObjectType (type: Type) {
36482
36483
const hasInstancePropertyName = getPropertyNameForKnownSymbolName("hasInstance");
36483
36484
const hasInstanceProperty = getPropertyOfObjectType(type, hasInstancePropertyName);
36484
- if (hasInstanceProperty && hasInstanceProperty !== getPropertyOfObjectType(globalFunctionType, hasInstancePropertyName) ) {
36485
+ if (hasInstanceProperty) {
36485
36486
const hasInstancePropertyType = getTypeOfSymbol(hasInstanceProperty);
36486
36487
if (hasInstancePropertyType && getSignaturesOfType(hasInstancePropertyType, SignatureKind.Call).length !== 0) {
36487
36488
return hasInstancePropertyType;
@@ -36537,24 +36538,43 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
36537
36538
// if rightType is an object type with a custom `[Symbol.hasInstance]` method, then it is potentially
36538
36539
// valid on the right-hand side of the `instanceof` operator. This allows normal `object` types to
36539
36540
// 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
+ }
36550
36567
}
36551
36568
// 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))) {
36553
36570
// 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 :
36558
36578
Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type;
36559
36579
error(right, message);
36560
36580
}
0 commit comments