pub struct Analyzer<'scope, 'b> {
Show 15 fields env: Env, pub(crate) cm: Arc<SourceMap>, comments: StcComments, pub mutations: Option<Mutations>, storage: Storage<'b>, export_equals_span: Span, scope: Scope<'scope>, ctx: Ctx, loader: &'b dyn Load, pub(crate) config: InnerConfig, cur_facts: Facts, mapped_type_param_name: Vec<Id>, debugger: Option<Debugger>, data: Box<AnalyzerData>, destructure_count: Rc<Cell<DestructureId>>,
}
Expand description

Note: All methods named validate_* return Err iff it’s not recoverable.

Fields

env: Envcm: Arc<SourceMap>comments: StcCommentsmutations: Option<Mutations>

This is None only for .d.ts files.

storage: Storage<'b>export_equals_span: Spanscope: Scope<'scope>ctx: Ctxloader: &'b dyn Loadconfig: InnerConfigcur_facts: Factsmapped_type_param_name: Vec<Id>

Used while inferring types.

debugger: Option<Debugger>data: Box<AnalyzerData>destructure_count: Rc<Cell<DestructureId>>

Implementations

This handles the assignment to builtin types.

  • Handles assignment of Function types.
  • Handles assignment of various array types.
  • Handles assignment of promise types.

Returns true if the type can be casted to number if it’s in the rvalue position.

Methods to handle assignment to function types and constructor types.

class Base {}
class Derived extends Base {

}

declare var a: (b: Base) => {};
declare var b: (b: Derived) => { foo: string };

a = b;
b = a; // error
Note

We should distinguish assign failure due to type parameter instantiation with assign failure due to type element kind mismatch.

declare var a16: {
    new (x: {
        new (a: number): number;
        new (a?: number): number;
    }): number[];
    new (x: {
        new (a: boolean): boolean;
        new (a?: boolean): boolean;
    }): boolean[];
};
declare var b16: new <T>(x: (a: T) => T) => T[];
a16 = b16; // error
b16 = a16; // error


declare var a18: {
    new (x: {
        (a: number): number;
        (a: string): string;
    }): any[];
    new (x: {
        (a: boolean): boolean;
        (a: Date): Date;
    }): any[];
}
declare var b18: new <T>(x: (a: T) => T) => T[];
a18 = b18; // ok
b18 = a18; // ok

Assigns a parameter to another one. It may assign in reverse direction because of the rule 1. At the same time, it should not be reversed in some cases. (See rule 2)

Rule 1
declare let a: (parent: 'foo' | 'bar') => void
declare let b: (parent: 'bar') => void

a = b // error
b = a // ok

Valid assignment is foo | bar = bar, which is a.param[0] = b.param[0], but it doesn’t match b = a.

Rule 2
class Base {
    private foo!: string
}
class Derived extends Base {
    private bar!: string
}

declare var a: (y: Derived) => any;
declare var b: (y: Base) => any

a = b // ok
b = a // error

Valid assignment is Derived = Base, which is a.params[0] = b.param[0] and it matches a = b.

Notes
  • string is assignable to ...args: any[].

Implementation of assign_param.

Validation of parameter count

A parameter named this is excluded.

Rule
declare var a: (x: string) => any;
declare var b: (x: string, y: number) => any

a = b // error
b = a // ok

So, it’s an error if l.params.len() < r.params.len().

Implementation notes

We split string based on the literals.

:${string}:::${string}: = “:1:sss:s:s:s:s::s:s:”

For the code above, we try to find :, :::, :, while preserving orders.

After splitting, we can check if each element is assignable.

Ported from isValidTypeForTemplateLiteralPlaceholder of tsc

Ported from templateLiteralTypesDefinitelyUnrelated of tsc.

This method is called when lhs of assignment is interface or type literal.

interface A {}
let a: A = foo;
let b: { key: string } = foo;

This method assigns each property to corresponding property.

Because of overloads, this methods takes &[TypeElement] instead of TypeElement for lhs.

Implementation notes
Methods
Type parameters
interface T {
    f(x: number): void;
}
var t: T;
t = { f: <T>(x:T) => 1 };

This is valid.

Call signatures
    (s: string): void
    (s: number): void
}
declare var b: {
    (s: string): void
}


a = b // error
b = a // ok
Cases

Cases handled by this methods are

  • lhs = (["a", number] | ["b", number] | ["c", string]);
  • rhs = [("b" | "a"), 1];

TODO(kdy1): Use Cow

TODO(kdy1): Use Cow

Expands boolean to true | false.

Denies null and undefined. This method does not check for elements of union.

Used to validate assignments like a += b.

Assign right to left. You can just use default for AssignData.

Assign right to left. You can just use default for AssignData.

Assigns, but does not wrap error with [Error::AssignFailed].

Should be called only if to is not expandable.

Handles P in 'foo' | 'bar'. Note that 'foo' | 'bar' part should be passed as keys.

Currently only literals and unions are supported for keys.

Returns true for A | B | | C = A | B and similar cases.

Should be called iff lhs is a union type.

TODO(kdy1): I’m not sure about this.

Calculate the order of the evaluation of class members. This is used to avoid reevaluation if possible.

Note that this is not perfect, and if class methods references each other, we have to evaluate them again with any for references.

This method ignores order of class properties or parameter properties. So the length of returned vector can be smaller than length of members.

Note that the body constructor is analyzed.

TODO(kdy1): Implement this.

Should be called only from Validate<Class>.

This method combines setters and getters, and merge it just like a normal property.

If a class have an index signature, properties should be compatible with it.

TODO(kdy1): Instantiate fully

This method may remove SafeSubscriber from Subscriber | SafeSubscriber or downgrade the type, like converting Subscriber | SafeSubscriber into SafeSubscriber. This behavior is controlled by the mark applied while handling type facts related to call.

Remove SafeSubscriber from Subscriber | SafeSubscriber.

Returns the type of discriminant.

TODO(kdy1): Implement this.

Returns true if a body of switch always ends with return, throw or continue.

TODO(kdy1): Support break with other label.

While this type fact is in scope, the var named sym will be treated as ty.

If type_facts is None, this method calculates type facts created by 'foo' in obj.

Otherwise, this method calculates type facts created by if (a.foo) ;. In this case, this method tests if type_facts matches the type of property and returns never if it does not.

Handle implicit defaults.

Handle implicit defaults.

Handle implicit defaults.

Handle implicit defaults.

Handle implicit defaults.

Handle declaration merging. This method is used to avoid implementing same logic twice.

Handle declaration merging.

enumBasics.ts says

Enum object type is anonymous with properties of the enum type and numeric indexer.

and following is valid.

var e = E1;
var e: {
    readonly A: E1.A;
    readonly B: E1.B;
    readonly C: E1.C;
    readonly [n: number]: string;
};
var e: typeof E1;

Converts e in o[e] from the code below to 'a' | 'b'

enum E {
    A = 'a',
    B = 'b',
}

const o = { a: 1, b: 2 };
declare const e: E;
const a = o[e]

Expands an enum variant as a literal.

Currently noop because we need to know if a function is last item among overloads

Exports a type.

scope.register_type should be called before calling this method.

Note: We don’t freeze types at here because doing so may prevent proper finalization.

Exports a variable.

Get nth element from the iterator.

Parameters
try_next_value

If it’s true, this method will try ty.next().value.

Returns the type of iterator.next().value.

Example
// Note: feature.geometry can be undefined

function extractCoordinates(f: Feature): number[] {
    if (f.geometry?.type !== 'test') {
        return [];
    }
    return f.geometry.coordinates;
}

The condition in the if statement above will be true if f.geometry is undefined.

We have to check for inheritance.

class C1 {
    p1: string;
}
class C2 {
    p2: number;
}
class D1 extends C1 {
    p3: number;
}
var ctor2: C2 | D1;

var r2: D1 | C2 = ctor2 instanceof C1 && ctor2; // C2 | D1

in this case, we cannot store ctor2 as C1 because it would result in an error.

TODO(kdy1): Use Cow

narrowingConstrainedTypeVariable.ts

In the test, there’s function f2<T extends C, U extends D>(v: T | U) {}.

If we apply instanceof C to v, v becomes T. Note that C extends D and D extends C are true because both of C and D are empty classes.

Returns Ok(Some(v)) if this method has a special rule to handle type elements.

The right operand to be of type Any or a subtype of the ‘Function’ interface type.

We should create a type fact for foo in if (foo.type === 'bar');.

Returns (name, true_fact, false_fact).

Returns new type of the variable after comparison with ===.

Parameters
orig_ty

Original type of the variable.

Calculates the return type of a new /call expression.

This method check arguments

TODO(kdy1): Use Cow for obj_type

Parameters
  • expr: Can be default if argument does not include an arrow expression nor a function expression.

Returns ()

Search for members and returns if there’s a match

Returns None if nothing matched.

Returns the return type of function. This method should be called only for final step because it emits errors instead of returning them.

Note:

function isSubscriber(val: any): val is DummySubscriber;
const observerOrNext: () => void | Subscriber;
const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber();

should make type of subscriber SafeSubscriber, not Subscriber. I (kdy1) don’t know why.

This method return Err if call is invalid

Implementation notes

anyAssignabilityInInheritance.ts says any, not a subtype of number so it skips that overload, is a subtype of itself so it picks second (if truly ambiguous it would pick first overload)

Object literals in unions are normalized upon widening.

 var a = [{ a: 0 }, { a: 1, b: "x" }];

Type of a in the code above is { a: number, b?: undefined } | { a:number, b: string }.

If rhs is an union type, return type will be union.

{ a: number } + ( {b: number} | { c: number } ) => { a: number, b: number } | { a: number, c: number }

var unionTuple3: [number, string | number] = [10, "foo"];
var unionTuple4 = <[number, number]>unionTuple3;

is valid, while

var unionTuple3: [number, string | number] = [10, "foo"];
var unionTuple4: [number, number] = unionTuple3;

results in error.

Parameters
  • l: from
  • r: to

Returns true if a rhs expression of the assignment expression can be a tuple.

Check if key matches.

Parameters
  • declared: Key of declared property.

TODO(kdy1): Clarify this.

Expand type parameters using type_args.

Returned type reflects conditional type facts.

Returned type does not reflects conditional type facts. (like Truthy / exclusion)

TODO(kdy1): Expand type arguments if provided.

TODO(kdy1): Expand type arguments if provided.

Fill type arguments using default value.

If the referred type has default type parameter, we have to include it in function type of output (.d.ts)

TODO(kdy1): Handle recursive function

TODO(kdy1): Remove this.

Check if it’s okay to generalize ty.

Generic expander.

This methods handle special types like mapped type.

e.g. type BadNested = { x: T extends number ? T : string; }; T extends { [K in keyof BadNested]: BadNested[K]; } ? P : never;

z T extends { x: infer P extends number ? infer P : string; } ? P : never

Returns Some(true) if child extends parent.

Ported from inferFromMatchingTypes of tsc.

Ported from tsc.

Ported from inferToMultipleTypes of tsc.

Ported from inferFromTypes of tsc.

inferToMultipleTypesWithPriority

Ported from inferFromContravariantTypes of tsc.

Ported from getInferenceInfoForType of tsc.

Ported from getSingleTypeVariableFromIntersectionTypes of tsc.

Ported from inferToTemplateLiteralType of tsc.

Ported from inferTypesFromTemplateLiteralType of tsc.

Ported from inferFromLiteralPartsToTemplateLiteral of tsc.

This function infers from the text parts and type parts of a source literal to a target template literal. The number of text parts is always one more than the number of type parts, and a source string literal is treated as a source with one text part and zero type parts. The function returns an array of inferred string or template literal types corresponding to the placeholders in the target template literal, or undefined if the source doesn’t match the target.

We first check that the starting source text part matches the starting target text part, and that the ending source text part ends matches the ending target text part. We then iterate through the remaining target text parts, finding a match for each in the source and inferring string or template literal types created from the segments of the source that occur between the matches. During this iteration, seg holds the index of the current text part in the sourceTexts array and pos holds the current character position in the current text part. Consider inference from type <<${string}>.<${number}-${number}>> to type <${string}.${string}>, i.e. sourceTexts = [‘<<’, ‘>.<’, ‘-’, ‘>>’] sourceTypes = [string, number, number] target.texts = [‘<’, ‘.’, ‘>’] We first match ‘<’ in the target to the start of ‘<<’ in the source and ‘>’ in the target to the end of ‘>>’ in the source. The first match for the ‘.’ in target occurs at character 1 in the source text part at index 1, and thus the first inference is the template literal type <${string}>. The remainder of the source makes up the second inference, the template literal type <${number}-${number}>.

Rules
Type literal

If one of type literal is specified according to the metadata, type inference is done.

See:

declare function f<T>(...items: T[]): T;
declare let data: { a: 1, b: "abc", c: true };
declare let data2: { b: "foo", c: true };

// Not specified
let e1 = f({ a: 1, b: 2 }, { a: "abc" }, {});
let e2 = f({}, { a: "abc" }, { a: 1, b: 2 });

// Type inference is done if at least one element is specified.
let e3 = f(data, { a: 2 }); // Error
let e4 = f({ a: 2 }, data); // Error
let e5 = f(data, data2); // Error

Infer types, using param and arg.

Handle some special builtin types

Compare fields.

Returns Ok(true) if this method know how to infer types.

TODO(kdy1): Handle union

Prevent generalizations if a type parameter extends literal.

Type inference for arguments.

This method accepts Option<&TypeParamInstantiation> because user may provide only some of type arguments.

Handles infer U.

Inference rule
  1. We iterate over parameters and arguments in order.

  2. If newly inferred type is not compatible with the previous one, we don’t store it. compatible here means the previous type is assignable to the newly inferred type.

  3. If there’s any or unknown, those are used because all types are compatible with them.

If any and unknown co-exist, the last one is selected.

  1. {} and an empty interface work just like any or unknown. It’s because almost all types are compatible with it, so the same rule applies. But any or unknown is preferred over {}.

  2. If a parameter of a closure has an explicit type, the compatibility rule applies. But some types like the built-in Object are exceptions and those are ignored. i.e. The inferred types are not changed to Object.

  3. The return type of a closure does not have effect on the inference, iff it’s a direct function expression.

Postprocess
  1. If there was noe error and if there’s no constraints like extends string nor extends number, the inferred types are generalized.

function foo<T>(x: { bar: T; baz: T }) {
    return x;
}

declare function fn1(): void;
declare function f2(): string;

declare class C1 {
    prop: string
}

declare class C2 {
    c2prop: number
}

interface I1 {
    s: string
}

declare const us: unique symbol
declare var i1: I1

declare var c1: C1
declare var c2: C2


declare var n: number

foo({ bar: 1, baz: '' }); // Error on baz (number is selected)
foo({ bar: '', baz: 1 }); // Error on baz (string is selected)
foo({ bar: '', baz: n }); // Error on baz (string is selected)
foo({ bar: Symbol.iterator, baz: 5 }) // Error on baz (symbol is selected)
foo({ bar: us, baz: 5 }) // Error on baz (unique symbol is selected)


foo({ bar: [], baz: '' }); // Error on bar (string is selected)
foo({ bar: {}, baz: '' }); // Error on bar (string is selected)

declare var u: string | number
foo({ bar: 1, baz: u }) // Ok
foo({ bar: {}, baz: u }) // Error on bar (string | number is selected)

foo({ bar: i1, baz: 5 }) // Error on baz (I1 is selected)
foo({ bar: 5, baz: i1 }) // Error on baz (number is selected)


foo({ bar: 5, baz: fn1 }) // Error on baz (number is selected)
foo({ bar: 5, baz: i1 }) // Error on baz (number is selected)

foo({ bar: 1, baz: c2 }) // Error on baz (number is selected)
foo({ bar: c1, baz: 1 }) // Error on baz (C1 is selected)
foo({ bar: c1, baz: c2 }) // Error on baz (C1 is selected)
foo({ bar: i1, baz: c1 }) // Error on baz (I1 is selected)
foo({ bar: c1, baz: i1 }) // Error on baz (C1 is selected)


function arr<T>(x: T[]) {
    return x;
}

arr([1, '']); // Ok
arr(['', 1]); // Ok
arr([Symbol.iterator, 5]) // Ok
arr([us, 5]) // Ok


arr([[], '']); // Ok
arr([{}, '']); // Ok

arr([1, u]) // Ok
arr([{}, u]) // Ok

Infer types, so that param has same type as arg.

TODO(kdy1): Optimize

Handles renaming of the type parameters.

A special method is require code like

function foo() {
    return a;
}

const a = 5;
const b = foo();

Returns (the order of evaluation, skipped index). This methods is used to handle hoisting properly.

Example

The method will return [1, 0] for the code below.

function foo() {
    return bar();
}

function bar (){
    return 1;
}K
Note

This function prioritize types in order of

  • no deps
  • resolvable (non-circular)
  • others

a.ts:

import { B } from './b';
export type C = 5 | 10;
export type B = A;

b.ts:

import A from './a';
export type C = 5 | 10;
export type B = A;

Returns (dep_module, dep_types) if an import is valid, and returns (cur_mod_id, empty_data) on import errors.

TODO: Make this returns None when import failed

Computed properties should not use type parameters defined by the declaring class.

See: computedPropertyNames32_ES5.ts

Ported from isTypeRelatedTo of tsc.

TODO: Implement

Ported from isSimpleTypeRelatedTo of tsc.

TODO: Implement

Ported from checkTypeRelatedTo of tsc.

Expand this contained in ty.

Convert type parameters declared in dead scopes to {}.

This does not touch type parameters declared in parent scopes and this method should be called when a type had escaped a scope.

In this way, we can handle both of cases below.

Invalid assignment.
function foo<T, U>() {
    var a: T;
    var b: U;
    a = b; // This is wrong.
}

This case is handled because type parameters are not touched (while we are analyzing function) by the method.

Escaping
function foo<T>() {}
function bar<T>() {}

var a = foo(); // type is {}
a = bar();

This method is called at the end of each call and each T is converted to {} even though span hygiene differs.

TODO(kdy1): Rename to declare_vars

Parameters
actual

The type of actual value.

default

The type of default value specified by an assignment pattern.

Overrides a variable. Used for updating types.

Expands

  • Type alias

// TODO(kdy1): Add an option to expand union (this is required to assign)

  • expand_union should be true if you are going to use it in assignment, and false if you are going to use it in user-visible stuffs (e.g. type annotation for .d.ts file)

Expands the type if it’s Type::Ref.

This should be called after calling register_type.

TODO(kdy1): Restore this(?)

If allow_multiple is true and is_override is false, the value type is updated only if it’s temporary type (like typeof foo while validating foo).

Returns Err if overload is wrong.

TODO(kdy1): Merge with declare_vars_*

Mark ty as not expanded by default.

Mark ty as expandable. This has higher precedence than prevent_expansion.

We evaluate loop bodies multiple time. But actually we don’t report errors

If type does not change due to a loop, we evaluate

This method returns Generator if yield is found.

Validate that parent interfaces are all resolved.

These methods are ported from tsc.

isTypeCloselyMatchedBy of tsc.

isTypeOrBaseIdenticalTo of tsc.

isTypeIdenticalTo of tsc.

Ported from isTypeAssignableTo of tsc.

Ported from isValidNumberString of tsc.

Ported from isValidBigIntString of tsc.

Ported from isMemberOfStringMapping of tsc.

Get IndexSignature from ty, if there’s one.

Evaluates keyof operator.

Parameters
ty

Should be operand of keyof.

Implementation note for Tuple
 
function f15<T extends string[], U extends T>(k0: keyof T, k1: keyof [...T], k2: keyof [...U], k3: keyof [1, 2, ...T]) {
    k0 = 'length';
    k1 = 'length';
    k2 = 'length';
    k0 = 'slice';
    k1 = 'slice';
    k2 = 'slice';
    k3 = '0';
    k3 = '1';
    k3 = '2';  // Error
}

Required because mapped type can specified by user, like

declare const a: Partial<Foo>;

TODO(kdy1): Handle index signatures.

TODO(kdy1): Optimize

Evaluate a type and convert it to keys.

Used for types like 'foo' | 'bar' or alias of them.

Get keys of ty as a property name.

This methods normalizes a type.

Changed types.
Span

If span is provided, it will be used for types created by the method. Otherwise the span of the original type is used.

Exclude types from ty using type facts with key name, for the current scope.

Collect all class members, including inherited members.

Parameters
excluded

Members of base class.

Note: span is only used while expanding type (to prevent panic) in the case of Type::Ref.

This is used to determine form of els. Each type has a value. e.g. 1 for TypeElement::Call.

Utility method to convert a class member to a type element.

This method is used while inferring types and while assigning type element to class member or vice versa.

Exclude excluded from ty

Subclasses
class B {}
class P {}
class C extends P {}

declare let a: C | B


if (!(a instanceof P)) {
    // At here, we can deduce that `a` is `B`.
    // To use the fact that `a` is not `P`, we check for the parent type of `ty
}

We precompute all type declarations in the scope, using this method.

Prints type for visualization testing.

span and callee is used only for error reporting.

Make instance of ty. In case of error, error will be reported to user and ty will be returned.

TODO(kdy1): Use Cow

TODO(kdy1): Use Cow

Hook is invoked with self (not child) after op.

Used for debugging. Returns the line and column of span.lo in form of (line, column).

TODO(kdy1): Note: This method preserves Type::Ref in some cases.

Those are preserved if

  • it’s Promise

Trait Implementations

Order:

  1. static properties
  2. static methods, using dependency graph.
  3. TsParamProp in constructors.
  4. Properties from top to bottom.
  5. Others, using dependency graph.

Done

Done

Order of evaluation is important to handle infer types correctly.

We don’t visit enum variants to allow

       const enum E {
           a = 10,
           b = a,
           c = (a+1),
           e,
           d = ~e,
           f = a << 2 >> 1,
           g = a << 2 >>> 1,
           h = a | b
       }

Done

We analyze dependencies between type parameters, and fold parameter in topological order.

NOTE: We does not dig into with statements.

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Delegates to Validate<T>

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The alignment of pointer.
The type for initializers.
Initializes a with the given initializer. Read more
Dereferences the given pointer. Read more
Mutably dereferences the given pointer. Read more
Drops the object pointed to by the given pointer. Read more
Should always be Self
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more