All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.Promises Maven / Gradle / Ivy

/*
 * Copyright 2018 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp;

import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.common.collect.ImmutableList;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.TemplateType;
import com.google.javascript.rhino.jstype.TemplateTypeMap;
import com.google.javascript.rhino.jstype.UnionType;

/**
 * Models different Javascript Promise-related operations
 */
final class Promises {

  private Promises() {}

  /**
   * If this object is known to be an IThenable, returns the type it resolves to.
   *
   * 

Returns unknown otherwise. * *

(This is different from {@code getResolvedType}, which will attempt to model the then type * of an expression after calling Promise.resolve() on it. */ static final JSType getTemplateTypeOfThenable(JSTypeRegistry registry, JSType maybeThenable) { // Without ".restrictByNotNullOrUndefined" we'd get the unknown type for "?IThenable" TemplateType templateType = registry.getIThenableTemplate(); return maybeThenable // Without ".restrictByNotNullOrUndefined" we'd get the unknown type for "?IThenable" .restrictByNotNullOrUndefined() .getTemplateTypeMap() .getResolvedTemplateType(templateType); } /** * Returns the type of `await [expr]`. * *

This is equivalent to the type of `result` in `Promise.resolve([expr]).then(result => ` * *

For example: * *

{@code !Promise} becomes {@code number} * *

{@code !IThenable} becomes {@code number} * *

{@code string} becomes {@code string} * *

{@code (!Promise|string)} becomes {@code (number|string)} * *

{@code ?Promise} becomes {@code (null|number)} */ static final JSType getResolvedType(JSTypeRegistry registry, JSType type) { if (type.isUnknownType()) { return type; } if (type.isUnionType()) { UnionType.Builder unionTypeBuilder = UnionType.builder(registry); for (JSType alternate : type.toMaybeUnionType().getAlternates()) { unionTypeBuilder.addAlternate(getResolvedType(registry, alternate)); } return unionTypeBuilder.build(); } // If we can find the "IThenable" template key (which is true for Promise and IThenable), return // the resolved value. e.g. for "!Promise" return "string". TemplateTypeMap templates = type.getTemplateTypeMap(); if (templates.hasTemplateKey(registry.getIThenableTemplate())) { // Call getResolvedPromiseType again in case someone does something unusual like // !Promise> // TODO(lharker): we don't need to handle this case and should report an error for this in a // type annotation (not here, maybe in TypeCheck). A Promise cannot resolve to another Promise return getResolvedType( registry, templates.getResolvedTemplateType(registry.getIThenableTemplate())); } // Awaiting anything with a ".then" property (other than IThenable, handled above) should return // unknown, rather than the type itself. if (type.isSubtypeOf(registry.getNativeType(JSTypeNative.THENABLE_TYPE))) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return type; } /** * Wraps the given type in an IThenable. * *

If the given type is already IThenable it is first unwrapped. For example: * *

{@code number} becomes {@code IThenable} * *

{@code IThenable} becomes {@code IThenable} * *

{@code Promise} becomes {@code IThenable} * *

{@code IThenable|string} becomes {@code IThenable} * *

{@code IThenable|IThenable} becomes {@code IThenable} */ static final JSType wrapInIThenable(JSTypeRegistry registry, JSType maybeThenable) { // Unwrap for simplicity first in the event it is a thenable. JSType unwrapped = getResolvedType(registry, maybeThenable); return registry.createTemplatizedType( registry.getNativeObjectType(JSTypeNative.I_THENABLE_TYPE), unwrapped); } /** * Synthesizes a type representing the legal types of a return expression within async code * (i.e.`Promise` callbacks, async functions) based on the expected return type of that code. * *

The return type will generally be a union but may not be in the case of top-like types. If * the expected return type is a union, any synchronous elements will be dropped, since they can * never occur. For example: * *

    *
  • `!Promise` => `number|!IThenable` *
  • `number` => `?` *
  • `number|!Promise` => `string|!IThenable` *
  • `!IThenable|!Promise` => `number|string|!IThenable` *
  • `!IThenable` => `number|string|!IThenable` *
  • `?` => `?` *
  • `*` => `?` *
*/ static final JSType createAsyncReturnableType(JSTypeRegistry registry, JSType maybeThenable) { JSType unknownType = registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); ObjectType iThenableType = registry.getNativeObjectType(JSTypeNative.I_THENABLE_TYPE); JSType iThenableOfUnknownType = registry.createTemplatizedType(iThenableType, unknownType); ImmutableList alternates = maybeThenable.isUnionType() ? maybeThenable.toMaybeUnionType().getAlternates() : ImmutableList.of(maybeThenable); ImmutableList asyncTemplateAlternates = alternates.stream() .filter((t) -> t.isSubtypeOf(iThenableOfUnknownType)) // Discard "synchronous" types. .map((t) -> getTemplateTypeOfThenable(registry, t)) // Unwrap "asynchronous" types. .collect(toImmutableList()); if (asyncTemplateAlternates.isEmpty()) { return unknownType; } JSType asyncTemplateUnion = registry.createUnionType(asyncTemplateAlternates); return registry.createUnionType( asyncTemplateUnion, registry.createTemplatizedType(iThenableType, asyncTemplateUnion)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy