src/com/google/javascript/jscomp/TypeCheck.java
/**
   * Checks whether type is useful as the key of an object property access.
   * This means it should be either stringifiable or a symbol.
   * Stringifiable types are types that can be converted to string
   * and give unique results for different objects. For example objects have native toString()
   * method that on chrome returns "[object Object]" for all objects making it useless when used
   * as keys. At the same time native types like numbers can be safely converted to strings and
   * used as keys. Also user might have provided custom toString() methods for a class making it
   * suitable for using as key.
   */
Checks whether type is useful as the key of an object property access.
This means it should be either stringifiable or a symbol.
Stringifiable types are types that can be converted to string
and give unique results for different objects. For example objects have native toString()
method that on chrome returns "[object Object]" for all objects making it useless when used
as keys. At the same time native types like numbers can be safely converted to strings and
used as keys. Also user might have provided custom toString() methods for a class making it
suitable for using as key.
Checks whether type is useful as the key of an object property access . This means it should be either stringifiable or a symbol . Stringifiable types are types that can be converted to string and give unique results for different objects . For example objects have native toString () method that on chrome returns " [ object Object ] " for all objects making it useless when used as keys . At the same time native types like numbers can be safely converted to strings and used as keys . Also user might have provided custom toString () methods for a class making it suitable for using as key .
private boolean isReasonableObjectPropertyKey(JSType type) {
    if (type.isUnknownType()
        || type.isNumber()
        || type.isString()
        || type.isSymbol()
        || type.isBooleanObjectType()
        || type.isBooleanValueType()
        || type.isDateType()
        || type.isRegexpType()
        || type.isInterface()
        || type.isRecordType()
        || type.isNullType()
        || type.isVoidType()) {
      return true;
    }
    if (type.toMaybeEnumElementType() != null) {
      return isReasonableObjectPropertyKey(type.toMaybeEnumElementType().getPrimitiveType());
    }
    if (type.isArrayType()) {
      return true;
    }
    if (type.isTemplatizedType()) {
      TemplatizedType templatizedType = type.toMaybeTemplatizedType();
      if (templatizedType.getReferencedType().isArrayType()) {
        return isReasonableObjectPropertyKey(templatizedType.getTemplateTypes().get(0));
      }
    }
    if (type instanceof NamedType) {
      return isReasonableObjectPropertyKey(((NamedType) type).getReferencedType());
    }
    if (type.isUnionType()) {
      for (JSType alternateType : type.toMaybeUnionType().getAlternates()) {
        if (!isReasonableObjectPropertyKey(alternateType)) {
          return false;
        }
      }
      return true;
    }
    if (type.isObject()) {
      ObjectType objectType = type.toMaybeObjectType();
      JSType constructor = objectType.getConstructor();
      if (constructor != null && constructor.isInterface()) {
        return true;
      }
      return classHasToString(objectType);
    }
    return false;
  }
private boolean isReasonableObjectPropertyKey ( JSType type ) { if ( type . isUnknownType ( ) || type . isNumber ( ) || type . isString ( ) || type . isSymbol ( ) || type . isBooleanObjectType ( ) || type . isBooleanValueType ( ) || type . isDateType ( ) || type . isRegexpType ( ) || type . isInterface ( ) || type . isRecordType ( ) || type . isNullType ( ) || type . isVoidType ( ) ) { return true ; } if ( type . toMaybeEnumElementType ( ) != null ) { return isReasonableObjectPropertyKey ( type . toMaybeEnumElementType ( ) . getPrimitiveType ( ) ) ; } if ( type . isArrayType ( ) ) { return true ; } if ( type . isTemplatizedType ( ) ) { TemplatizedType templatizedType = type . toMaybeTemplatizedType ( ) ; if ( templatizedType . getReferencedType ( ) . isArrayType ( ) ) { return isReasonableObjectPropertyKey ( templatizedType . getTemplateTypes ( ) . get ( 0 ) ) ; } } if ( type instanceof NamedType ) { return isReasonableObjectPropertyKey ( ( ( NamedType ) type ) . getReferencedType ( ) ) ; } if ( type . isUnionType ( ) ) { for ( JSType alternateType : type . toMaybeUnionType ( ) . getAlternates ( ) ) { if ( ! isReasonableObjectPropertyKey ( alternateType ) ) { return false ; } } return true ; } if ( type . isObject ( ) ) { ObjectType objectType = type . toMaybeObjectType ( ) ; JSType constructor = objectType . getConstructor ( ) ; if ( constructor != null && constructor . isInterface ( ) ) { return true ; } return classHasToString ( objectType ) ; } return false ; }
Checks whether type is useful as the key of an object property access.
Checks whether type is useful as the key of an object property access .
// Check built-in types // For enums check that underlying type is stringifiable. // Array is stringifiable if it doesn't have template type or if it does have it, the template // type must be also stringifiable. // Good: Array, Array.<number> // Bad: Array.<!Object> // Named types are usually @typedefs. For such types we need to check underlying type specified // in @typedef annotation. // For union type every alternate must be stringifiable. // Handle interfaces and classes. // Interfaces considered stringifiable as user might implement toString() method in // classes-implementations. // This is user-defined class so check if it has custom toString() method.