diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c4e5af9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,92 @@ +{ + "name": "ts2php", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "10.12.0", + "resolved": "http://registry.npm.baidu-int.com/@types%2fnode/-/node-10.12.0.tgz", + "integrity": "sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ==" + }, + "arrify": { + "version": "1.0.1", + "resolved": "http://registry.npm.baidu-int.com/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "http://registry.npm.baidu-int.com/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "diff": { + "version": "3.5.0", + "resolved": "http://registry.npm.baidu-int.com/diff/-/diff-3.5.0.tgz", + "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=" + }, + "make-error": { + "version": "1.3.5", + "resolved": "http://registry.npm.baidu-int.com/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==" + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npm.baidu-int.com/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npm.baidu-int.com/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npm.baidu-int.com/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "http://registry.npm.baidu-int.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "http://registry.npm.baidu-int.com/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "requires": { + "buffer-from": "1.1.1", + "source-map": "0.6.1" + } + }, + "ts-node": { + "version": "7.0.1", + "resolved": "http://registry.npm.baidu-int.com/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "requires": { + "arrify": "1.0.1", + "buffer-from": "1.1.1", + "diff": "3.5.0", + "make-error": "1.3.5", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.5.9", + "yn": "2.0.0" + } + }, + "typescript": { + "version": "3.1.3", + "resolved": "http://registry.npm.baidu-int.com/typescript/-/typescript-3.1.3.tgz", + "integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA==" + }, + "yn": { + "version": "2.0.0", + "resolved": "http://registry.npm.baidu-int.com/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=" + } + } +} diff --git a/package.json b/package.json index 74963c7..84722ef 100644 --- a/package.json +++ b/package.json @@ -11,5 +11,10 @@ "url": "ssh://meixuguang@icode.baidu.com:8235/baidu/atom/ts2php" }, "author": "meixuguang", - "license": "UNLICENSED" + "license": "UNLICENSED", + "dependencies": { + "@types/node": "^10.12.0", + "ts-node": "^7.0.1", + "typescript": "^3.1.3" + } } diff --git a/sample/atomWiseUtils.d.ts b/sample/atomWiseUtils.d.ts new file mode 100644 index 0000000..fa4e90e --- /dev/null +++ b/sample/atomWiseUtils.d.ts @@ -0,0 +1,14 @@ +interface tcLink { + type: string; + tcUrl: string; + sfUrl?: string; + options?: string; + log?: string; +} + +declare namespace atomWiseUtils { + function makeTcLink(url: string, options?: any): tcLink; + let tplData: any; +} + +export = atomWiseUtils; \ No newline at end of file diff --git a/sample/index.ts b/sample/index.ts new file mode 100644 index 0000000..eca730d --- /dev/null +++ b/sample/index.ts @@ -0,0 +1,5 @@ +import {makeTcLink, tplData} from './atomWiseUtils'; + + + +tplData.src = makeTcLink(tplData.src); diff --git a/src/core.ts b/src/core.ts new file mode 100644 index 0000000..538ceba --- /dev/null +++ b/src/core.ts @@ -0,0 +1,2129 @@ + +import { + showSyntaxKind +} from './utilities'; + +import { + Node +} from 'typescript'; + +/** + * Type of objects whose values are all of the same type. + * The `in` and `for-in` operators can *not* be safely used, + * since `Object.prototype` may be modified by outside code. + */ +export interface MapLike { + [index: string]: T; +} + +export interface SortedArray extends Array { + " __sortedArrayBrand": any; +} + + +/** ES6 Map interface, only read methods included. */ +export interface ReadonlyMap { + get(key: string): T | undefined; + has(key: string): boolean; + forEach(action: (value: T, key: string) => void): void; + readonly size: number; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[string, T]>; +} + +/** ES6 Map interface. */ +export interface Map extends ReadonlyMap { + set(key: string, value: T): this; + delete(key: string): boolean; + clear(): void; +} + +/** ES6 Iterator type. */ +export interface Iterator { + next(): { value: T, done: false } | { value: never, done: true }; +} + +/** Array that is only intended to be pushed to, never read. */ +export interface Push { + push(...values: T[]): void; +} + +/* @internal */ +export type EqualityComparer = (a: T, b: T) => boolean; + +/* @internal */ +export type Comparer = (a: T, b: T) => Comparison; + +/* @internal */ +export const enum Comparison { + LessThan = -1, + EqualTo = 0, + GreaterThan = 1 +} + +export const emptyArray: never[] = [] as never[]; + +/** Create a MapLike with good performance. */ +function createDictionaryObject(): MapLike { + const map = Object.create(/*prototype*/ null); // tslint:disable-line:no-null-keyword + + // Using 'delete' on an object causes V8 to put the object in dictionary mode. + // This disables creation of hidden classes, which are expensive when an object is + // constantly changing shape. + map.__ = undefined; + delete map.__; + + return map; +} + +/** Create a new map. If a template object is provided, the map will copy entries from it. */ +export function createMap(): Map { + return new MapCtr(); +} + +export function createMapFromEntries(entries: [string, T][]): Map { + const map = createMap(); + for (const [key, value] of entries) { + map.set(key, value); + } + return map; +} + +export function createMapFromTemplate(template: MapLike): Map { + const map: Map = new MapCtr(); + + // Copies keys/values from template. Note that for..in will not throw if + // template is undefined, and instead will just exit the loop. + for (const key in template) { + if (hasOwnProperty.call(template, key)) { + map.set(key, template[key]); + } + } + + return map; +} + +// The global Map object. This may not be available, so we must test for it. +declare const Map: { new (): Map } | undefined; +// Internet Explorer's Map doesn't support iteration, so don't use it. +// tslint:disable-next-line no-in-operator variable-name +export const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap(); + +// Keep the class inside a function so it doesn't get compiled if it's not used. +function shimMap(): { new (): Map } { + + class MapIterator { + private data: MapLike; + private keys: ReadonlyArray; + private index = 0; + private selector: (data: MapLike, key: string) => U; + constructor(data: MapLike, selector: (data: MapLike, key: string) => U) { + this.data = data; + this.selector = selector; + this.keys = Object.keys(data); + } + + public next(): { value: U, done: false } | { value: never, done: true } { + const index = this.index; + if (index < this.keys.length) { + this.index++; + return { value: this.selector(this.data, this.keys[index]), done: false }; + } + return { value: undefined as never, done: true }; + } + } + + return class implements Map { + private data = createDictionaryObject(); + public size = 0; + + get(key: string): T | undefined { + return this.data[key]; + } + + set(key: string, value: T): this { + if (!this.has(key)) { + this.size++; + } + this.data[key] = value; + return this; + } + + has(key: string): boolean { + // tslint:disable-next-line:no-in-operator + return key in this.data; + } + + delete(key: string): boolean { + if (this.has(key)) { + this.size--; + delete this.data[key]; + return true; + } + return false; + } + + clear(): void { + this.data = createDictionaryObject(); + this.size = 0; + } + + keys(): Iterator { + return new MapIterator(this.data, (_data, key) => key); + } + + values(): Iterator { + return new MapIterator(this.data, (data, key) => data[key]); + } + + entries(): Iterator<[string, T]> { + return new MapIterator(this.data, (data, key) => [key, data[key]] as [string, T]); + } + + forEach(action: (value: T, key: string) => void): void { + for (const key in this.data) { + action(this.data[key], key); + } + } + }; +} + +export function length(array: ReadonlyArray | undefined): number { + return array ? array.length : 0; +} + +/** + * Iterates through 'array' by index and performs the callback on each element of array until the callback + * returns a truthy value, then returns that value. + * If no such value is found, the callback is applied to each element of array and undefined is returned. + */ +export function forEach(array: ReadonlyArray | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { + if (array) { + for (let i = 0; i < array.length; i++) { + const result = callback(array[i], i); + if (result) { + return result; + } + } + } + return undefined; +} + +/** Like `forEach`, but suitable for use with numbers and strings (which may be falsy). */ +export function firstDefined(array: ReadonlyArray | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { + if (array === undefined) { + return undefined; + } + + for (let i = 0; i < array.length; i++) { + const result = callback(array[i], i); + if (result !== undefined) { + return result; + } + } + return undefined; +} + +export function firstDefinedIterator(iter: Iterator, callback: (element: T) => U | undefined): U | undefined { + while (true) { + const { value, done } = iter.next(); + if (done) { + return undefined; + } + const result = callback(value); + if (result !== undefined) { + return result; + } + } +} + +export function zipWith(arrayA: ReadonlyArray, arrayB: ReadonlyArray, callback: (a: T, b: U, index: number) => V): V[] { + const result: V[] = []; + Debug.assertEqual(arrayA.length, arrayB.length); + for (let i = 0; i < arrayA.length; i++) { + result.push(callback(arrayA[i], arrayB[i], i)); + } + return result; +} + +export function zipToIterator(arrayA: ReadonlyArray, arrayB: ReadonlyArray): Iterator<[T, U]> { + Debug.assertEqual(arrayA.length, arrayB.length); + let i = 0; + return { + next() { + if (i === arrayA.length) { + return { value: undefined as never, done: true }; + } + i++; + return { value: [arrayA[i - 1], arrayB[i - 1]], done: false }; + } + }; +} + +export function zipToMap(keys: ReadonlyArray, values: ReadonlyArray): Map { + Debug.assert(keys.length === values.length); + const map = createMap(); + for (let i = 0; i < keys.length; ++i) { + map.set(keys[i], values[i]); + } + return map; +} + +/** + * Iterates through `array` by index and performs the callback on each element of array until the callback + * returns a falsey value, then returns false. + * If no such value is found, the callback is applied to each element of array and `true` is returned. + */ +export function every(array: ReadonlyArray, callback: (element: T, index: number) => boolean): boolean { + if (array) { + for (let i = 0; i < array.length; i++) { + if (!callback(array[i], i)) { + return false; + } + } + } + + return true; +} + +/** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */ +export function find(array: ReadonlyArray, predicate: (element: T, index: number) => element is U): U | undefined; +export function find(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): T | undefined; +export function find(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): T | undefined { + for (let i = 0; i < array.length; i++) { + const value = array[i]; + if (predicate(value, i)) { + return value; + } + } + return undefined; +} + +export function findLast(array: ReadonlyArray, predicate: (element: T, index: number) => element is U): U | undefined; +export function findLast(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): T | undefined; +export function findLast(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): T | undefined { + for (let i = array.length - 1; i >= 0; i--) { + const value = array[i]; + if (predicate(value, i)) { + return value; + } + } + return undefined; +} + +/** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. */ +export function findIndex(array: ReadonlyArray, predicate: (element: T, index: number) => boolean, startIndex?: number): number { + for (let i = startIndex || 0; i < array.length; i++) { + if (predicate(array[i], i)) { + return i; + } + } + return -1; +} + +export function findLastIndex(array: ReadonlyArray, predicate: (element: T, index: number) => boolean, startIndex?: number): number { + for (let i = startIndex === undefined ? array.length - 1 : startIndex; i >= 0; i--) { + if (predicate(array[i], i)) { + return i; + } + } + return -1; +} + +/** + * Returns the first truthy result of `callback`, or else fails. + * This is like `forEach`, but never returns undefined. + */ +export function findMap(array: ReadonlyArray, callback: (element: T, index: number) => U | undefined): U { + for (let i = 0; i < array.length; i++) { + const result = callback(array[i], i); + if (result) { + return result; + } + } + return Debug.fail(); +} + +export function contains(array: ReadonlyArray | undefined, value: T, equalityComparer: EqualityComparer = equateValues): boolean { + if (array) { + for (const v of array) { + if (equalityComparer(v, value)) { + return true; + } + } + } + return false; +} + +export function arraysEqual(a: ReadonlyArray, b: ReadonlyArray, equalityComparer: EqualityComparer = equateValues): boolean { + return a.length === b.length && a.every((x, i) => equalityComparer(x, b[i])); +} + +export function indexOfAnyCharCode(text: string, charCodes: ReadonlyArray, start?: number): number { + for (let i = start || 0; i < text.length; i++) { + if (contains(charCodes, text.charCodeAt(i))) { + return i; + } + } + return -1; +} + +export function countWhere(array: ReadonlyArray, predicate: (x: T, i: number) => boolean): number { + let count = 0; + if (array) { + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (predicate(v, i)) { + count++; + } + } + } + return count; +} + +/** + * Filters an array by a predicate function. Returns the same array instance if the predicate is + * true for all elements, otherwise returns a new array instance containing the filtered subset. + */ +export function filter(array: T[], f: (x: T) => x is U): U[]; +export function filter(array: T[], f: (x: T) => boolean): T[]; +export function filter(array: ReadonlyArray, f: (x: T) => x is U): ReadonlyArray; +export function filter(array: ReadonlyArray, f: (x: T) => boolean): ReadonlyArray; +export function filter(array: T[] | undefined, f: (x: T) => x is U): U[] | undefined; +export function filter(array: T[] | undefined, f: (x: T) => boolean): T[] | undefined; +export function filter(array: ReadonlyArray | undefined, f: (x: T) => x is U): ReadonlyArray | undefined; +export function filter(array: ReadonlyArray | undefined, f: (x: T) => boolean): ReadonlyArray | undefined; +export function filter(array: ReadonlyArray | undefined, f: (x: T) => boolean): ReadonlyArray | undefined { + if (array) { + const len = array.length; + let i = 0; + while (i < len && f(array[i])) i++; + if (i < len) { + const result = array.slice(0, i); + i++; + while (i < len) { + const item = array[i]; + if (f(item)) { + result.push(item); + } + i++; + } + return result; + } + } + return array; +} + +export function filterMutate(array: T[], f: (x: T, i: number, array: T[]) => boolean): void { + let outIndex = 0; + for (let i = 0; i < array.length; i++) { + if (f(array[i], i, array)) { + array[outIndex] = array[i]; + outIndex++; + } + } + array.length = outIndex; +} + +export function clear(array: {}[]): void { + array.length = 0; +} + +export function map(array: ReadonlyArray, f: (x: T, i: number) => U): U[]; +export function map(array: ReadonlyArray | undefined, f: (x: T, i: number) => U): U[] | undefined; +export function map(array: ReadonlyArray | undefined, f: (x: T, i: number) => U): U[] | undefined { + let result: U[] | undefined; + if (array) { + result = []; + for (let i = 0; i < array.length; i++) { + result.push(f(array[i], i)); + } + } + return result; +} + + +export function mapIterator(iter: Iterator, mapFn: (x: T) => U): Iterator { + return { + next() { + const iterRes = iter.next(); + return iterRes.done ? iterRes : { value: mapFn(iterRes.value), done: false }; + } + }; +} + +// Maps from T to T and avoids allocation if all elements map to themselves +export function sameMap(array: T[], f: (x: T, i: number) => T): T[]; +export function sameMap(array: ReadonlyArray, f: (x: T, i: number) => T): ReadonlyArray; +export function sameMap(array: T[] | undefined, f: (x: T, i: number) => T): T[] | undefined; +export function sameMap(array: ReadonlyArray | undefined, f: (x: T, i: number) => T): ReadonlyArray | undefined; +export function sameMap(array: ReadonlyArray | undefined, f: (x: T, i: number) => T): ReadonlyArray | undefined { + if (array) { + for (let i = 0; i < array.length; i++) { + const item = array[i]; + const mapped = f(item, i); + if (item !== mapped) { + const result = array.slice(0, i); + result.push(mapped); + for (i++; i < array.length; i++) { + result.push(f(array[i], i)); + } + return result; + } + } + } + return array; +} + +/** + * Flattens an array containing a mix of array or non-array elements. + * + * @param array The array to flatten. + */ +export function flatten(array: ReadonlyArray | undefined>): T[]; +export function flatten(array: ReadonlyArray | undefined> | undefined): T[] | undefined; +export function flatten(array: ReadonlyArray | undefined> | undefined): T[] | undefined { + let result: T[] | undefined; + if (array) { + result = []; + for (const v of array) { + if (v) { + if (isArray(v)) { + addRange(result, v); + } + else { + result.push(v); + } + } + } + } + + return result; +} + +/** + * Maps an array. If the mapped value is an array, it is spread into the result. + * + * @param array The array to map. + * @param mapfn The callback used to map the result into one or more values. + */ +export function flatMap(array: ReadonlyArray, mapfn: (x: T, i: number) => U | ReadonlyArray | undefined): U[]; +export function flatMap(array: ReadonlyArray | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray | undefined): U[] | undefined; +export function flatMap(array: ReadonlyArray | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray | undefined): U[] | undefined { + let result: U[] | undefined; + if (array) { + result = []; + for (let i = 0; i < array.length; i++) { + const v = mapfn(array[i], i); + if (v) { + if (isArray(v)) { + addRange(result, v); + } + else { + result.push(v); + } + } + } + } + return result; +} + +export function flatMapIterator(iter: Iterator, mapfn: (x: T) => ReadonlyArray | Iterator | undefined): Iterator { + const first = iter.next(); + if (first.done) { + return emptyIterator; + } + let currentIter = getIterator(first.value); + return { + next() { + while (true) { + const currentRes = currentIter.next(); + if (!currentRes.done) { + return currentRes; + } + const iterRes = iter.next(); + if (iterRes.done) { + return iterRes; + } + currentIter = getIterator(iterRes.value); + } + }, + }; + + function getIterator(x: T): Iterator { + const res = mapfn(x); + return res === undefined ? emptyIterator : isArray(res) ? arrayIterator(res) : res; + } +} + +/** + * Maps an array. If the mapped value is an array, it is spread into the result. + * Avoids allocation if all elements map to themselves. + * + * @param array The array to map. + * @param mapfn The callback used to map the result into one or more values. + */ +export function sameFlatMap(array: T[], mapfn: (x: T, i: number) => T | ReadonlyArray): T[]; +export function sameFlatMap(array: ReadonlyArray, mapfn: (x: T, i: number) => T | ReadonlyArray): ReadonlyArray; +export function sameFlatMap(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] { + let result: T[] | undefined; + if (array) { + for (let i = 0; i < array.length; i++) { + const item = array[i]; + const mapped = mapfn(item, i); + if (result || item !== mapped || isArray(mapped)) { + if (!result) { + result = array.slice(0, i); + } + if (isArray(mapped)) { + addRange(result, mapped); + } + else { + result.push(mapped); + } + } + } + } + return result || array; +} + +export function mapAllOrFail(array: ReadonlyArray, mapFn: (x: T, i: number) => U | undefined): U[] | undefined { + const result: U[] = []; + for (let i = 0; i < array.length; i++) { + const mapped = mapFn(array[i], i); + if (mapped === undefined) { + return undefined; + } + result.push(mapped); + } + return result; +} + +export function mapDefined(array: ReadonlyArray | undefined, mapFn: (x: T, i: number) => U | undefined): U[] { + const result: U[] = []; + if (array) { + for (let i = 0; i < array.length; i++) { + const mapped = mapFn(array[i], i); + if (mapped !== undefined) { + result.push(mapped); + } + } + } + return result; +} + +export function mapDefinedIterator(iter: Iterator, mapFn: (x: T) => U | undefined): Iterator { + return { + next() { + while (true) { + const res = iter.next(); + if (res.done) { + return res; + } + const value = mapFn(res.value); + if (value !== undefined) { + return { value, done: false }; + } + } + } + }; +} + +export const emptyIterator: Iterator = { next: () => ({ value: undefined as never, done: true }) }; + +export function singleIterator(value: T): Iterator { + let done = false; + return { + next() { + const wasDone = done; + done = true; + return wasDone ? { value: undefined as never, done: true } : { value, done: false }; + } + }; +} + +/** + * Maps contiguous spans of values with the same key. + * + * @param array The array to map. + * @param keyfn A callback used to select the key for an element. + * @param mapfn A callback used to map a contiguous chunk of values to a single value. + */ +export function spanMap(array: ReadonlyArray, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[]; +export function spanMap(array: ReadonlyArray | undefined, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] | undefined; +export function spanMap(array: ReadonlyArray | undefined, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] | undefined { + let result: U[] | undefined; + if (array) { + result = []; + const len = array.length; + let previousKey: K | undefined; + let key: K | undefined; + let start = 0; + let pos = 0; + while (start < len) { + while (pos < len) { + const value = array[pos]; + key = keyfn(value, pos); + if (pos === 0) { + previousKey = key; + } + else if (key !== previousKey) { + break; + } + + pos++; + } + + if (start < pos) { + const v = mapfn(array.slice(start, pos), previousKey!, start, pos); + if (v) { + result.push(v); + } + + start = pos; + } + + previousKey = key; + pos++; + } + } + + return result; +} + +export function mapEntries(map: ReadonlyMap, f: (key: string, value: T) => [string, U]): Map; +export function mapEntries(map: ReadonlyMap | undefined, f: (key: string, value: T) => [string, U]): Map | undefined; +export function mapEntries(map: ReadonlyMap | undefined, f: (key: string, value: T) => [string, U]): Map | undefined { + if (!map) { + return undefined; + } + + const result = createMap(); + map.forEach((value, key) => { + const [newKey, newValue] = f(key, value); + result.set(newKey, newValue); + }); + return result; +} +export function some(array: ReadonlyArray | undefined): array is ReadonlyArray; +export function some(array: ReadonlyArray | undefined, predicate: (value: T) => boolean): boolean; +export function some(array: ReadonlyArray | undefined, predicate?: (value: T) => boolean): boolean { + if (array) { + if (predicate) { + for (const v of array) { + if (predicate(v)) { + return true; + } + } + } + else { + return array.length > 0; + } + } + return false; +} + +/** Calls the callback with (start, afterEnd) index pairs for each range where 'pred' is true. */ +export function getRangesWhere(arr: ReadonlyArray, pred: (t: T) => boolean, cb: (start: number, afterEnd: number) => void): void { + let start: number | undefined; + for (let i = 0; i < arr.length; i++) { + if (pred(arr[i])) { + start = start === undefined ? i : start; + } + else { + if (start !== undefined) { + cb(start, i); + start = undefined; + } + } + } + if (start !== undefined) cb(start, arr.length); +} + +export function concatenate(array1: T[], array2: T[]): T[]; +export function concatenate(array1: ReadonlyArray, array2: ReadonlyArray): ReadonlyArray; +export function concatenate(array1: T[] | undefined, array2: T[] | undefined): T[]; +export function concatenate(array1: ReadonlyArray | undefined, array2: ReadonlyArray | undefined): ReadonlyArray; +export function concatenate(array1: T[], array2: T[]): T[] { + if (!some(array2)) return array1; + if (!some(array1)) return array2; + return [...array1, ...array2]; +} + +function deduplicateRelational(array: ReadonlyArray, equalityComparer: EqualityComparer, comparer: Comparer) { + // Perform a stable sort of the array. This ensures the first entry in a list of + // duplicates remains the first entry in the result. + const indices = array.map((_, i) => i); + stableSortIndices(array, indices, comparer); + + let last = array[indices[0]]; + const deduplicated: number[] = [indices[0]]; + for (let i = 1; i < indices.length; i++) { + const index = indices[i]; + const item = array[index]; + if (!equalityComparer(last, item)) { + deduplicated.push(index); + last = item; + } + } + + // restore original order + deduplicated.sort(); + return deduplicated.map(i => array[i]); +} + +function deduplicateEquality(array: ReadonlyArray, equalityComparer: EqualityComparer) { + const result: T[] = []; + for (const item of array) { + pushIfUnique(result, item, equalityComparer); + } + return result; +} + +/** + * Deduplicates an unsorted array. + * @param equalityComparer An optional `EqualityComparer` used to determine if two values are duplicates. + * @param comparer An optional `Comparer` used to sort entries before comparison, though the + * result will remain in the original order in `array`. + */ +export function deduplicate(array: ReadonlyArray, equalityComparer: EqualityComparer, comparer?: Comparer): T[] { + return array.length === 0 ? [] : + array.length === 1 ? array.slice() : + comparer ? deduplicateRelational(array, equalityComparer, comparer) : + deduplicateEquality(array, equalityComparer); +} + +/** + * Deduplicates an array that has already been sorted. + */ +function deduplicateSorted(array: ReadonlyArray, comparer: EqualityComparer | Comparer): T[] { + if (array.length === 0) return []; + + let last = array[0]; + const deduplicated: T[] = [last]; + for (let i = 1; i < array.length; i++) { + const next = array[i]; + switch (comparer(next, last)) { + // equality comparison + case true: + + // relational comparison + case Comparison.EqualTo: + continue; + + case Comparison.LessThan: + // If `array` is sorted, `next` should **never** be less than `last`. + return Debug.fail("Array is unsorted."); + } + + deduplicated.push(last = next); + } + + return deduplicated; +} + +export function insertSorted(array: SortedArray, insert: T, compare: Comparer): void { + if (array.length === 0) { + array.push(insert); + return; + } + + const insertIndex = binarySearch(array, insert, identity, compare); + if (insertIndex < 0) { + array.splice(~insertIndex, 0, insert); + } +} + +export function sortAndDeduplicate(array: ReadonlyArray, comparer: Comparer, equalityComparer?: EqualityComparer) { + return deduplicateSorted(sort(array, comparer), equalityComparer || comparer); +} + +export function arrayIsEqualTo(array1: ReadonlyArray | undefined, array2: ReadonlyArray | undefined, equalityComparer: (a: T, b: T, index: number) => boolean = equateValues): boolean { + if (!array1 || !array2) { + return array1 === array2; + } + + if (array1.length !== array2.length) { + return false; + } + + for (let i = 0; i < array1.length; i++) { + if (!equalityComparer(array1[i], array2[i], i)) { + return false; + } + } + + return true; +} + +/** + * Compacts an array, removing any falsey elements. + */ +export function compact(array: T[]): T[]; +export function compact(array: ReadonlyArray): ReadonlyArray; +export function compact(array: T[]): T[] { + let result: T[] | undefined; + if (array) { + for (let i = 0; i < array.length; i++) { + const v = array[i]; + if (result || !v) { + if (!result) { + result = array.slice(0, i); + } + if (v) { + result.push(v); + } + } + } + } + return result || array; +} + +/** + * Gets the relative complement of `arrayA` with respect to `arrayB`, returning the elements that + * are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted + * based on the provided comparer. + */ +export function relativeComplement(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer): T[] | undefined { + if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB; + const result: T[] = []; + loopB: for (let offsetA = 0, offsetB = 0; offsetB < arrayB.length; offsetB++) { + if (offsetB > 0) { + // Ensure `arrayB` is properly sorted. + Debug.assertGreaterThanOrEqual(comparer(arrayB[offsetB], arrayB[offsetB - 1]), Comparison.EqualTo); + } + + loopA: for (const startA = offsetA; offsetA < arrayA.length; offsetA++) { + if (offsetA > startA) { + // Ensure `arrayA` is properly sorted. We only need to perform this check if + // `offsetA` has changed since we entered the loop. + Debug.assertGreaterThanOrEqual(comparer(arrayA[offsetA], arrayA[offsetA - 1]), Comparison.EqualTo); + } + + switch (comparer(arrayB[offsetB], arrayA[offsetA])) { + case Comparison.LessThan: + // If B is less than A, B does not exist in arrayA. Add B to the result and + // move to the next element in arrayB without changing the current position + // in arrayA. + result.push(arrayB[offsetB]); + continue loopB; + case Comparison.EqualTo: + // If B is equal to A, B exists in arrayA. Move to the next element in + // arrayB without adding B to the result or changing the current position + // in arrayA. + continue loopB; + case Comparison.GreaterThan: + // If B is greater than A, we need to keep looking for B in arrayA. Move to + // the next element in arrayA and recheck. + continue loopA; + } + } + } + return result; +} + +export function sum, K extends string>(array: ReadonlyArray, prop: K): number { + let result = 0; + for (const v of array) { + result += v[prop]; + } + return result; +} + +/** + * Appends a value to an array, returning the array. + * + * @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array + * is created if `value` was appended. + * @param value The value to append to the array. If `value` is `undefined`, nothing is + * appended. + */ +export function append(to: T[], value: T | undefined): T[]; +export function append(to: T[] | undefined, value: T): T[]; +export function append(to: T[] | undefined, value: T | undefined): T[] | undefined; +export function append(to: Push, value: T | undefined): void; +export function append(to: T[], value: T | undefined): T[] | undefined { + if (value === undefined) return to; + if (to === undefined) return [value]; + to.push(value); + return to; +} + +/** + * Gets the actual offset into an array for a relative offset. Negative offsets indicate a + * position offset from the end of the array. + */ +function toOffset(array: ReadonlyArray, offset: number) { + return offset < 0 ? array.length + offset : offset; +} + +/** + * Appends a range of value to an array, returning the array. + * + * @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array + * is created if `value` was appended. + * @param from The values to append to the array. If `from` is `undefined`, nothing is + * appended. If an element of `from` is `undefined`, that element is not appended. + * @param start The offset in `from` at which to start copying values. + * @param end The offset in `from` at which to stop copying values (non-inclusive). + */ +export function addRange(to: T[], from: ReadonlyArray | undefined, start?: number, end?: number): T[]; +export function addRange(to: T[] | undefined, from: ReadonlyArray | undefined, start?: number, end?: number): T[] | undefined; +export function addRange(to: T[] | undefined, from: ReadonlyArray | undefined, start?: number, end?: number): T[] | undefined { + if (from === undefined || from.length === 0) return to; + if (to === undefined) return from.slice(start, end); + start = start === undefined ? 0 : toOffset(from, start); + end = end === undefined ? from.length : toOffset(from, end); + for (let i = start; i < end && i < from.length; i++) { + if (from[i] !== undefined) { + to.push(from[i]); + } + } + return to; +} + +/** + * @return Whether the value was added. + */ +export function pushIfUnique(array: T[], toAdd: T, equalityComparer?: EqualityComparer): boolean { + if (contains(array, toAdd, equalityComparer)) { + return false; + } + else { + array.push(toAdd); + return true; + } +} + +/** + * Unlike `pushIfUnique`, this can take `undefined` as an input, and returns a new array. + */ +export function appendIfUnique(array: T[] | undefined, toAdd: T, equalityComparer?: EqualityComparer): T[] { + if (array) { + pushIfUnique(array, toAdd, equalityComparer); + return array; + } + else { + return [toAdd]; + } +} + +function stableSortIndices(array: ReadonlyArray, indices: number[], comparer: Comparer) { + // sort indices by value then position + indices.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)); +} + +/** + * Returns a new sorted array. + */ +export function sort(array: ReadonlyArray, comparer: Comparer): T[] { + return array.slice().sort(comparer); +} + +export function arrayIterator(array: ReadonlyArray): Iterator { + let i = 0; + return { next: () => { + if (i === array.length) { + return { value: undefined as never, done: true }; + } + else { + i++; + return { value: array[i - 1], done: false }; + } + }}; +} + +/** + * Stable sort of an array. Elements equal to each other maintain their relative position in the array. + */ +export function stableSort(array: ReadonlyArray, comparer: Comparer) { + const indices = array.map((_, i) => i); + stableSortIndices(array, indices, comparer); + return indices.map(i => array[i]); +} + +export function rangeEquals(array1: ReadonlyArray, array2: ReadonlyArray, pos: number, end: number) { + while (pos < end) { + if (array1[pos] !== array2[pos]) { + return false; + } + pos++; + } + return true; +} + +/** + * Returns the element at a specific offset in an array if non-empty, `undefined` otherwise. + * A negative offset indicates the element should be retrieved from the end of the array. + */ +export function elementAt(array: ReadonlyArray | undefined, offset: number): T | undefined { + if (array) { + offset = toOffset(array, offset); + if (offset < array.length) { + return array[offset]; + } + } + return undefined; +} + +/** + * Returns the first element of an array if non-empty, `undefined` otherwise. + */ +export function firstOrUndefined(array: ReadonlyArray): T | undefined { + return array.length === 0 ? undefined : array[0]; +} + +export function first(array: ReadonlyArray): T { + Debug.assert(array.length !== 0); + return array[0]; +} + +/** + * Returns the last element of an array if non-empty, `undefined` otherwise. + */ +export function lastOrUndefined(array: ReadonlyArray): T | undefined { + return array.length === 0 ? undefined : array[array.length - 1]; +} + +export function last(array: ReadonlyArray): T { + Debug.assert(array.length !== 0); + return array[array.length - 1]; +} + +/** + * Returns the only element of an array if it contains only one element, `undefined` otherwise. + */ +export function singleOrUndefined(array: ReadonlyArray | undefined): T | undefined { + return array && array.length === 1 + ? array[0] + : undefined; +} + +/** + * Returns the only element of an array if it contains only one element; otheriwse, returns the + * array. + */ +export function singleOrMany(array: T[]): T | T[]; +export function singleOrMany(array: ReadonlyArray): T | ReadonlyArray; +export function singleOrMany(array: T[] | undefined): T | T[] | undefined; +export function singleOrMany(array: ReadonlyArray | undefined): T | ReadonlyArray | undefined; +export function singleOrMany(array: ReadonlyArray | undefined): T | ReadonlyArray | undefined { + return array && array.length === 1 + ? array[0] + : array; +} + +export function replaceElement(array: ReadonlyArray, index: number, value: T): T[] { + const result = array.slice(0); + result[index] = value; + return result; +} + +/** + * Performs a binary search, finding the index at which `value` occurs in `array`. + * If no such index is found, returns the 2's-complement of first index at which + * `array[index]` exceeds `value`. + * @param array A sorted array whose first element must be no larger than number + * @param value The value to be searched for in the array. + * @param keySelector A callback used to select the search key from `value` and each element of + * `array`. + * @param keyComparer A callback used to compare two keys in a sorted array. + * @param offset An offset into `array` at which to start the search. + */ +export function binarySearch(array: ReadonlyArray, value: T, keySelector: (v: T) => U, keyComparer: Comparer, offset?: number): number { + if (!array || array.length === 0) { + return -1; + } + + let low = offset || 0; + let high = array.length - 1; + const key = keySelector(value); + while (low <= high) { + const middle = low + ((high - low) >> 1); + const midKey = keySelector(array[middle]); + switch (keyComparer(midKey, key)) { + case Comparison.LessThan: + low = middle + 1; + break; + case Comparison.EqualTo: + return middle; + case Comparison.GreaterThan: + high = middle - 1; + break; + } + } + + return ~low; +} + +export function reduceLeft(array: ReadonlyArray | undefined, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; +export function reduceLeft(array: ReadonlyArray, f: (memo: T, value: T, i: number) => T): T | undefined; +export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T | undefined { + if (array && array.length > 0) { + const size = array.length; + if (size > 0) { + let pos = start === undefined || start < 0 ? 0 : start; + const end = count === undefined || pos + count > size - 1 ? size - 1 : pos + count; + let result: T; + if (arguments.length <= 2) { + result = array[pos]; + pos++; + } + else { + result = initial!; + } + while (pos <= end) { + result = f(result, array[pos], pos); + pos++; + } + return result; + } + } + return initial; +} + +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Indicates whether a map-like contains an own property with the specified key. + * + * @param map A map-like. + * @param key A property key. + */ +export function hasProperty(map: MapLike, key: string): boolean { + return hasOwnProperty.call(map, key); +} + +/** + * Gets the value of an owned property in a map-like. + * + * @param map A map-like. + * @param key A property key. + */ +export function getProperty(map: MapLike, key: string): T | undefined { + return hasOwnProperty.call(map, key) ? map[key] : undefined; +} + +/** + * Gets the owned, enumerable property keys of a map-like. + */ +export function getOwnKeys(map: MapLike): string[] { + const keys: string[] = []; + for (const key in map) { + if (hasOwnProperty.call(map, key)) { + keys.push(key); + } + } + + return keys; +} + +export function getOwnValues(sparseArray: T[]): T[] { + const values: T[] = []; + for (const key in sparseArray) { + if (hasOwnProperty.call(sparseArray, key)) { + values.push(sparseArray[key]); + } + } + + return values; +} + +/** Shims `Array.from`. */ +export function arrayFrom(iterator: Iterator, map: (t: T) => U): U[]; +export function arrayFrom(iterator: Iterator): T[]; +export function arrayFrom(iterator: Iterator, map?: (t: any) => any): any[] { + const result: any[] = []; + for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { + result.push(map ? map(value) : value); + } + return result; +} + + +export function assign(t: T, ...args: (T | undefined)[]) { + for (const arg of args) { + for (const p in arg!) { + if (hasProperty(arg!, p)) { + t![p] = arg![p]; // TODO: GH#23368 + } + } + } + return t; +} + +/** + * Performs a shallow equality comparison of the contents of two map-likes. + * + * @param left A map-like whose properties should be compared. + * @param right A map-like whose properties should be compared. + */ +export function equalOwnProperties(left: MapLike | undefined, right: MapLike | undefined, equalityComparer: EqualityComparer = equateValues) { + if (left === right) return true; + if (!left || !right) return false; + for (const key in left) { + if (hasOwnProperty.call(left, key)) { + if (!hasOwnProperty.call(right, key)) return false; + if (!equalityComparer(left[key], right[key])) return false; + } + } + + for (const key in right) { + if (hasOwnProperty.call(right, key)) { + if (!hasOwnProperty.call(left, key)) return false; + } + } + + return true; +} + +/** + * Creates a map from the elements of an array. + * + * @param array the array of input elements. + * @param makeKey a function that produces a key for a given element. + * + * This function makes no effort to avoid collisions; if any two elements produce + * the same key with the given 'makeKey' function, then the element with the higher + * index in the array will be the one associated with the produced key. + */ +export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string | undefined): Map; +export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string | undefined, makeValue: (value: T) => U): Map; +export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string | undefined, makeValue: (value: T) => T | U = identity): Map { + const result = createMap(); + for (const value of array) { + const key = makeKey(value); + if (key !== undefined) result.set(key, makeValue(value)); + } + return result; +} + +export function arrayToNumericMap(array: ReadonlyArray, makeKey: (value: T) => number): T[]; +export function arrayToNumericMap(array: ReadonlyArray, makeKey: (value: T) => number, makeValue: (value: T) => U): U[]; +export function arrayToNumericMap(array: ReadonlyArray, makeKey: (value: T) => number, makeValue: (value: T) => T | U = identity): (T | U)[] { + const result: (T | U)[] = []; + for (const value of array) { + result[makeKey(value)] = makeValue(value); + } + return result; +} + +export function arrayToMultiMap(values: ReadonlyArray, makeKey: (value: T) => string): MultiMap; +export function arrayToMultiMap(values: ReadonlyArray, makeKey: (value: T) => string, makeValue: (value: T) => U): MultiMap; +export function arrayToMultiMap(values: ReadonlyArray, makeKey: (value: T) => string, makeValue: (value: T) => T | U = identity): MultiMap { + const result = createMultiMap(); + for (const value of values) { + result.add(makeKey(value), makeValue(value)); + } + return result; +} + +export function group(values: ReadonlyArray, getGroupId: (value: T) => string): ReadonlyArray> { + return arrayFrom(arrayToMultiMap(values, getGroupId).values()); +} + +export function clone(object: T): T { + const result: any = {}; + for (const id in object) { + if (hasOwnProperty.call(object, id)) { + result[id] = (object)[id]; + } + } + return result; +} + +export function extend(first: T1, second: T2): T1 & T2 { + const result: T1 & T2 = {}; + for (const id in second) { + if (hasOwnProperty.call(second, id)) { + (result as any)[id] = (second as any)[id]; + } + } + + for (const id in first) { + if (hasOwnProperty.call(first, id)) { + (result as any)[id] = (first as any)[id]; + } + } + + return result; +} + +export interface MultiMap extends Map { + /** + * Adds the value to an array of values associated with the key, and returns the array. + * Creates the array if it does not already exist. + */ + add(key: string, value: T): T[]; + /** + * Removes a value from an array of values associated with the key. + * Does not preserve the order of those values. + * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. + */ + remove(key: string, value: T): void; +} + +export function createMultiMap(): MultiMap { + const map = createMap() as MultiMap; + map.add = multiMapAdd; + map.remove = multiMapRemove; + return map; +} +function multiMapAdd(this: MultiMap, key: string, value: T) { + let values = this.get(key); + if (values) { + values.push(value); + } + else { + this.set(key, values = [value]); + } + return values; +} +function multiMapRemove(this: MultiMap, key: string, value: T) { + const values = this.get(key); + if (values) { + unorderedRemoveItem(values, value); + if (!values.length) { + this.delete(key); + } + } +} + +/** + * Tests whether a value is an array. + */ +export function isArray(value: any): value is ReadonlyArray<{}> { + return Array.isArray ? Array.isArray(value) : value instanceof Array; +} + +export function toArray(value: T | T[]): T[]; +export function toArray(value: T | ReadonlyArray): ReadonlyArray; +export function toArray(value: T | T[]): T[] { + return isArray(value) ? value : [value]; +} + +/** + * Tests whether a value is string + */ +export function isString(text: unknown): text is string { + return typeof text === "string"; +} +export function isNumber(x: unknown): x is number { + return typeof x === "number"; +} + +export function tryCast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined; +export function tryCast(value: T, test: (value: T) => boolean): T | undefined; +export function tryCast(value: T, test: (value: T) => boolean): T | undefined { + return value !== undefined && test(value) ? value : undefined; +} + +export function cast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut { + if (value !== undefined && test(value)) return value; + + return Debug.fail(`Invalid cast. The supplied value ${value} did not pass the test '${Debug.getFunctionName(test)}'.`); +} + +/** Does nothing. */ +export function noop(_?: {} | null | undefined): void { } // tslint:disable-line no-empty + +/** Do nothing and return false */ +export function returnFalse(): false { return false; } + +/** Do nothing and return true */ +export function returnTrue(): true { return true; } + +/** Returns its argument. */ +export function identity(x: T) { return x; } + +/** Returns lower case string */ +export function toLowerCase(x: string) { return x.toLowerCase(); } + +/** Throws an error because a function is not implemented. */ +export function notImplemented(): never { + throw new Error("Not implemented"); +} + +export function memoize(callback: () => T): () => T { + let value: T; + return () => { + if (callback) { + value = callback(); + callback = undefined!; + } + return value; + }; +} + +/** + * High-order function, creates a function that executes a function composition. + * For example, `chain(a, b)` is the equivalent of `x => ((a', b') => y => b'(a'(y)))(a(x), b(x))` + * + * @param args The functions to chain. + */ +export function chain(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U; +export function chain(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U { + if (e) { + const args: ((t: T) => (u: U) => U)[] = []; + for (let i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } + + return t => compose(...map(args, f => f(t))); + } + else if (d) { + return t => compose(a(t), b(t), c(t), d(t)); + } + else if (c) { + return t => compose(a(t), b(t), c(t)); + } + else if (b) { + return t => compose(a(t), b(t)); + } + else if (a) { + return t => compose(a(t)); + } + else { + return _ => u => u; + } +} + +/** + * High-order function, composes functions. Note that functions are composed inside-out; + * for example, `compose(a, b)` is the equivalent of `x => b(a(x))`. + * + * @param args The functions to compose. + */ +export function compose(...args: ((t: T) => T)[]): (t: T) => T; +export function compose(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T { + if (e) { + const args: ((t: T) => T)[] = []; + for (let i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } + + return t => reduceLeft(args, (u, f) => f(u), t); + } + else if (d) { + return t => d(c(b(a(t)))); + } + else if (c) { + return t => c(b(a(t))); + } + else if (b) { + return t => b(a(t)); + } + else if (a) { + return t => a(t); + } + else { + return t => t; + } +} + +export const enum AssertionLevel { + None = 0, + Normal = 1, + Aggressive = 2, + VeryAggressive = 3, +} + +/** + * Safer version of `Function` which should not be called. + * Every function should be assignable to this, but this should not be assignable to every function. + */ +export type AnyFunction = (...args: never[]) => void; +export type AnyConstructor = new (...args: unknown[]) => unknown; + +export namespace Debug { + export let currentAssertionLevel = AssertionLevel.None; + export let isDebugging = false; + + export function shouldAssert(level: AssertionLevel): boolean { + return currentAssertionLevel >= level; + } + + export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void { + if (!expression) { + if (verboseDebugInfo) { + message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo()); + } + fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert); + } + } + + export function assertEqual(a: T, b: T, msg?: string, msg2?: string): void { + if (a !== b) { + const message = msg ? msg2 ? `${msg} ${msg2}` : msg : ""; + fail(`Expected ${a} === ${b}. ${message}`); + } + } + + export function assertLessThan(a: number, b: number, msg?: string): void { + if (a >= b) { + fail(`Expected ${a} < ${b}. ${msg || ""}`); + } + } + + export function assertLessThanOrEqual(a: number, b: number): void { + if (a > b) { + fail(`Expected ${a} <= ${b}`); + } + } + + export function assertGreaterThanOrEqual(a: number, b: number): void { + if (a < b) { + fail(`Expected ${a} >= ${b}`); + } + } + + export function fail(message?: string, stackCrawlMark?: AnyFunction): never { + debugger; + const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); + if ((Error).captureStackTrace) { + (Error).captureStackTrace(e, stackCrawlMark || fail); + } + throw e; + } + + export function assertDefined(value: T | null | undefined, message?: string): T { + if (value === undefined || value === null) return fail(message); + return value; + } + + export function assertEachDefined>(value: A, message?: string): A { + for (const v of value) { + assertDefined(v, message); + } + return value; + } + + export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never { + const detail = "kind" in member && "pos" in member ? "SyntaxKind: " + showSyntaxKind(member as Node) : JSON.stringify(member); + return fail(`${message} ${detail}`, stackCrawlMark || assertNever); + } + + export function getFunctionName(func: AnyFunction) { + if (typeof func !== "function") { + return ""; + } + else if (func.hasOwnProperty("name")) { + return (func).name; + } + else { + const text = Function.prototype.toString.call(func); + const match = /^function\s+([\w\$]+)\s*\(/.exec(text); + return match ? match[1] : ""; + } + } +} + +export function equateValues(a: T, b: T) { + return a === b; +} + +/** + * Compare the equality of two strings using a case-sensitive ordinal comparison. + * + * Case-sensitive comparisons compare both strings one code-point at a time using the integer + * value of each code-point after applying `toUpperCase` to each string. We always map both + * strings to their upper-case form as some unicode characters do not properly round-trip to + * lowercase (such as `ẞ` (German sharp capital s)). + */ +export function equateStringsCaseInsensitive(a: string, b: string) { + return a === b + || a !== undefined + && b !== undefined + && a.toUpperCase() === b.toUpperCase(); +} + +/** + * Compare the equality of two strings using a case-sensitive ordinal comparison. + * + * Case-sensitive comparisons compare both strings one code-point at a time using the + * integer value of each code-point. + */ +export function equateStringsCaseSensitive(a: string, b: string) { + return equateValues(a, b); +} + +function compareComparableValues(a: string | undefined, b: string | undefined): Comparison; +function compareComparableValues(a: number | undefined, b: number | undefined): Comparison; +function compareComparableValues(a: string | number | undefined, b: string | number | undefined) { + return a === b ? Comparison.EqualTo : + a === undefined ? Comparison.LessThan : + b === undefined ? Comparison.GreaterThan : + a < b ? Comparison.LessThan : + Comparison.GreaterThan; +} + +/** + * Compare two numeric values for their order relative to each other. + * To compare strings, use any of the `compareStrings` functions. + */ +export function compareValues(a: number | undefined, b: number | undefined): Comparison { + return compareComparableValues(a, b); +} + +export function min(a: T, b: T, compare: Comparer): T { + return compare(a, b) === Comparison.LessThan ? a : b; +} + +/** + * Compare two strings using a case-insensitive ordinal comparison. + * + * Ordinal comparisons are based on the difference between the unicode code points of both + * strings. Characters with multiple unicode representations are considered unequal. Ordinal + * comparisons provide predictable ordering, but place "a" after "B". + * + * Case-insensitive comparisons compare both strings one code-point at a time using the integer + * value of each code-point after applying `toUpperCase` to each string. We always map both + * strings to their upper-case form as some unicode characters do not properly round-trip to + * lowercase (such as `ẞ` (German sharp capital s)). + */ +export function compareStringsCaseInsensitive(a: string, b: string) { + if (a === b) return Comparison.EqualTo; + if (a === undefined) return Comparison.LessThan; + if (b === undefined) return Comparison.GreaterThan; + a = a.toUpperCase(); + b = b.toUpperCase(); + return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo; +} + +/** + * Compare two strings using a case-sensitive ordinal comparison. + * + * Ordinal comparisons are based on the difference between the unicode code points of both + * strings. Characters with multiple unicode representations are considered unequal. Ordinal + * comparisons provide predictable ordering, but place "a" after "B". + * + * Case-sensitive comparisons compare both strings one code-point at a time using the integer + * value of each code-point. + */ +export function compareStringsCaseSensitive(a: string | undefined, b: string | undefined): Comparison { + return compareComparableValues(a, b); +} + +export function getStringComparer(ignoreCase?: boolean) { + return ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive; +} + +/** + * Creates a string comparer for use with string collation in the UI. + */ +const createUIStringComparer = (() => { + let defaultComparer: Comparer | undefined; + let enUSComparer: Comparer | undefined; + + const stringComparerFactory = getStringComparerFactory(); + return createStringComparer; + + function compareWithCallback(a: string | undefined, b: string | undefined, comparer: (a: string, b: string) => number) { + if (a === b) return Comparison.EqualTo; + if (a === undefined) return Comparison.LessThan; + if (b === undefined) return Comparison.GreaterThan; + const value = comparer(a, b); + return value < 0 ? Comparison.LessThan : value > 0 ? Comparison.GreaterThan : Comparison.EqualTo; + } + + function createIntlCollatorStringComparer(locale: string | undefined): Comparer { + // Intl.Collator.prototype.compare is bound to the collator. See NOTE in + // http://www.ecma-international.org/ecma-402/2.0/#sec-Intl.Collator.prototype.compare + const comparer = new Intl.Collator(locale, { usage: "sort", sensitivity: "variant" }).compare; + return (a, b) => compareWithCallback(a, b, comparer); + } + + function createLocaleCompareStringComparer(locale: string | undefined): Comparer { + // if the locale is not the default locale (`undefined`), use the fallback comparer. + if (locale !== undefined) return createFallbackStringComparer(); + + return (a, b) => compareWithCallback(a, b, compareStrings); + + function compareStrings(a: string, b: string) { + return a.localeCompare(b); + } + } + + function createFallbackStringComparer(): Comparer { + // An ordinal comparison puts "A" after "b", but for the UI we want "A" before "b". + // We first sort case insensitively. So "Aaa" will come before "baa". + // Then we sort case sensitively, so "aaa" will come before "Aaa". + // + // For case insensitive comparisons we always map both strings to their + // upper-case form as some unicode characters do not properly round-trip to + // lowercase (such as `ẞ` (German sharp capital s)). + return (a, b) => compareWithCallback(a, b, compareDictionaryOrder); + + function compareDictionaryOrder(a: string, b: string) { + return compareStrings(a.toUpperCase(), b.toUpperCase()) || compareStrings(a, b); + } + + function compareStrings(a: string, b: string) { + return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo; + } + } + + function getStringComparerFactory() { + // If the host supports Intl, we use it for comparisons using the default locale. + if (typeof Intl === "object" && typeof Intl.Collator === "function") { + return createIntlCollatorStringComparer; + } + + // If the host does not support Intl, we fall back to localeCompare. + // localeCompare in Node v0.10 is just an ordinal comparison, so don't use it. + if (typeof String.prototype.localeCompare === "function" && + typeof String.prototype.toLocaleUpperCase === "function" && + "a".localeCompare("B") < 0) { + return createLocaleCompareStringComparer; + } + + // Otherwise, fall back to ordinal comparison: + return createFallbackStringComparer; + } + + function createStringComparer(locale: string | undefined) { + // Hold onto common string comparers. This avoids constantly reallocating comparers during + // tests. + if (locale === undefined) { + return defaultComparer || (defaultComparer = stringComparerFactory(locale)); + } + else if (locale === "en-US") { + return enUSComparer || (enUSComparer = stringComparerFactory(locale)); + } + else { + return stringComparerFactory(locale); + } + } +})(); + +let uiComparerCaseSensitive: Comparer | undefined; +let uiLocale: string | undefined; + +export function getUILocale() { + return uiLocale; +} + +export function setUILocale(value: string | undefined) { + if (uiLocale !== value) { + uiLocale = value; + uiComparerCaseSensitive = undefined; + } +} + +/** + * Compare two strings in a using the case-sensitive sort behavior of the UI locale. + * + * Ordering is not predictable between different host locales, but is best for displaying + * ordered data for UI presentation. Characters with multiple unicode representations may + * be considered equal. + * + * Case-sensitive comparisons compare strings that differ in base characters, or + * accents/diacritic marks, or case as unequal. + */ +export function compareStringsCaseSensitiveUI(a: string, b: string) { + const comparer = uiComparerCaseSensitive || (uiComparerCaseSensitive = createUIStringComparer(uiLocale)); + return comparer(a, b); +} + +export function compareProperties(a: T | undefined, b: T | undefined, key: K, comparer: Comparer): Comparison { + return a === b ? Comparison.EqualTo : + a === undefined ? Comparison.LessThan : + b === undefined ? Comparison.GreaterThan : + comparer(a[key], b[key]); +} + +/** True is greater than false. */ +export function compareBooleans(a: boolean, b: boolean): Comparison { + return compareValues(a ? 1 : 0, b ? 1 : 0); +} + +/** + * Given a name and a list of names that are *not* equal to the name, return a spelling suggestion if there is one that is close enough. + * Names less than length 3 only check for case-insensitive equality, not Levenshtein distance. + * + * If there is a candidate that's the same except for case, return that. + * If there is a candidate that's within one edit of the name, return that. + * Otherwise, return the candidate with the smallest Levenshtein distance, + * except for candidates: + * * With no name + * * Whose length differs from the target name by more than 0.34 of the length of the name. + * * Whose levenshtein distance is more than 0.4 of the length of the name + * (0.4 allows 1 substitution/transposition for every 5 characters, + * and 1 insertion/deletion at 3 characters) + */ +export function getSpellingSuggestion(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined { + const maximumLengthDifference = Math.min(2, Math.floor(name.length * 0.34)); + let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result isn't better than this, don't bother. + let bestCandidate: T | undefined; + let justCheckExactMatches = false; + const nameLowerCase = name.toLowerCase(); + for (const candidate of candidates) { + const candidateName = getName(candidate); + if (candidateName !== undefined && Math.abs(candidateName.length - nameLowerCase.length) <= maximumLengthDifference) { + const candidateNameLowerCase = candidateName.toLowerCase(); + if (candidateNameLowerCase === nameLowerCase) { + if (candidateName === name) { + continue; + } + return candidate; + } + if (justCheckExactMatches) { + continue; + } + if (candidateName.length < 3) { + // Don't bother, user would have noticed a 2-character name having an extra character + continue; + } + // Only care about a result better than the best so far. + const distance = levenshteinWithMax(nameLowerCase, candidateNameLowerCase, bestDistance - 1); + if (distance === undefined) { + continue; + } + if (distance < 3) { + justCheckExactMatches = true; + bestCandidate = candidate; + } + else { + Debug.assert(distance < bestDistance); // Else `levenshteinWithMax` should return undefined + bestDistance = distance; + bestCandidate = candidate; + } + } + } + return bestCandidate; +} + +function levenshteinWithMax(s1: string, s2: string, max: number): number | undefined { + let previous = new Array(s2.length + 1); + let current = new Array(s2.length + 1); + /** Represents any value > max. We don't care about the particular value. */ + const big = max + 1; + + for (let i = 0; i <= s2.length; i++) { + previous[i] = i; + } + + for (let i = 1; i <= s1.length; i++) { + const c1 = s1.charCodeAt(i - 1); + const minJ = i > max ? i - max : 1; + const maxJ = s2.length > max + i ? max + i : s2.length; + current[0] = i; + /** Smallest value of the matrix in the ith column. */ + let colMin = i; + for (let j = 1; j < minJ; j++) { + current[j] = big; + } + for (let j = minJ; j <= maxJ; j++) { + const dist = c1 === s2.charCodeAt(j - 1) + ? previous[j - 1] + : Math.min(/*delete*/ previous[j] + 1, /*insert*/ current[j - 1] + 1, /*substitute*/ previous[j - 1] + 2); + current[j] = dist; + colMin = Math.min(colMin, dist); + } + for (let j = maxJ + 1; j <= s2.length; j++) { + current[j] = big; + } + if (colMin > max) { + // Give up -- everything in this column is > max and it can't get better in future columns. + return undefined; + } + + const temp = previous; + previous = current; + current = temp; + } + + const res = previous[s2.length]; + return res > max ? undefined : res; +} + +export function endsWith(str: string, suffix: string): boolean { + const expectedPos = str.length - suffix.length; + return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos; +} + +export function removeSuffix(str: string, suffix: string): string { + return endsWith(str, suffix) ? str.slice(0, str.length - suffix.length) : str; +} + +export function tryRemoveSuffix(str: string, suffix: string): string | undefined { + return endsWith(str, suffix) ? str.slice(0, str.length - suffix.length) : undefined; +} + +export function stringContains(str: string, substring: string): boolean { + return str.indexOf(substring) !== -1; +} + +export function fileExtensionIs(path: string, extension: string): boolean { + return path.length > extension.length && endsWith(path, extension); +} + +export function fileExtensionIsOneOf(path: string, extensions: ReadonlyArray): boolean { + for (const extension of extensions) { + if (fileExtensionIs(path, extension)) { + return true; + } + } + + return false; +} + +/** + * Takes a string like "jquery-min.4.2.3" and returns "jquery" + */ +export function removeMinAndVersionNumbers(fileName: string) { + // Match a "." or "-" followed by a version number or 'min' at the end of the name + const trailingMinOrVersion = /[.-]((min)|(\d+(\.\d+)*))$/; + + // The "min" or version may both be present, in either order, so try applying the above twice. + return fileName.replace(trailingMinOrVersion, "").replace(trailingMinOrVersion, ""); +} + +/** Remove an item from an array, moving everything to its right one space left. */ +export function orderedRemoveItem(array: T[], item: T): boolean { + for (let i = 0; i < array.length; i++) { + if (array[i] === item) { + orderedRemoveItemAt(array, i); + return true; + } + } + return false; +} + +/** Remove an item by index from an array, moving everything to its right one space left. */ +export function orderedRemoveItemAt(array: T[], index: number): void { + // This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`. + for (let i = index; i < array.length - 1; i++) { + array[i] = array[i + 1]; + } + array.pop(); +} + +export function unorderedRemoveItemAt(array: T[], index: number): void { + // Fill in the "hole" left at `index`. + array[index] = array[array.length - 1]; + array.pop(); +} + +/** Remove the *first* occurrence of `item` from the array. */ +export function unorderedRemoveItem(array: T[], item: T) { + return unorderedRemoveFirstItemWhere(array, element => element === item); +} + +/** Remove the *first* element satisfying `predicate`. */ +function unorderedRemoveFirstItemWhere(array: T[], predicate: (element: T) => boolean) { + for (let i = 0; i < array.length; i++) { + if (predicate(array[i])) { + unorderedRemoveItemAt(array, i); + return true; + } + } + return false; +} + +export type GetCanonicalFileName = (fileName: string) => string; +export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): GetCanonicalFileName { + return useCaseSensitiveFileNames ? identity : toLowerCase; +} + +/** Represents a "prefix*suffix" pattern. */ +/* @internal */ +export interface Pattern { + prefix: string; + suffix: string; +} + +export function patternText({ prefix, suffix }: Pattern): string { + return `${prefix}*${suffix}`; +} + +/** + * Given that candidate matches pattern, returns the text matching the '*'. + * E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar" + */ +export function matchedText(pattern: Pattern, candidate: string): string { + Debug.assert(isPatternMatch(pattern, candidate)); + return candidate.substring(pattern.prefix.length, candidate.length - pattern.suffix.length); +} + +/** Return the object corresponding to the best pattern to match `candidate`. */ +export function findBestPatternMatch(values: ReadonlyArray, getPattern: (value: T) => Pattern, candidate: string): T | undefined { + let matchedValue: T | undefined; + // use length of prefix as betterness criteria + let longestMatchPrefixLength = -1; + + for (const v of values) { + const pattern = getPattern(v); + if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) { + longestMatchPrefixLength = pattern.prefix.length; + matchedValue = v; + } + } + + return matchedValue; +} + +export function startsWith(str: string, prefix: string): boolean { + return str.lastIndexOf(prefix, 0) === 0; +} + +export function removePrefix(str: string, prefix: string): string { + return startsWith(str, prefix) ? str.substr(prefix.length) : str; +} + +export function tryRemovePrefix(str: string, prefix: string, getCanonicalFileName: GetCanonicalFileName = identity): string | undefined { + return startsWith(getCanonicalFileName(str), getCanonicalFileName(prefix)) ? str.substring(prefix.length) : undefined; +} + +function isPatternMatch({ prefix, suffix }: Pattern, candidate: string) { + return candidate.length >= prefix.length + suffix.length && + startsWith(candidate, prefix) && + endsWith(candidate, suffix); +} + +export function and(f: (arg: T) => boolean, g: (arg: T) => boolean) { + return (arg: T) => f(arg) && g(arg); +} + +export function or(f: (arg: T) => boolean, g: (arg: T) => boolean): (arg: T) => boolean { + return arg => f(arg) || g(arg); +} + +export function assertType(_: T): void { } // tslint:disable-line no-empty + +export function singleElementArray(t: T | undefined): T[] | undefined { + return t === undefined ? undefined : [t]; +} + +export function enumerateInsertsAndDeletes(newItems: ReadonlyArray, oldItems: ReadonlyArray, comparer: (a: T, b: U) => Comparison, inserted: (newItem: T) => void, deleted: (oldItem: U) => void, unchanged?: (oldItem: U, newItem: T) => void) { + unchanged = unchanged || noop; + let newIndex = 0; + let oldIndex = 0; + const newLen = newItems.length; + const oldLen = oldItems.length; + while (newIndex < newLen && oldIndex < oldLen) { + const newItem = newItems[newIndex]; + const oldItem = oldItems[oldIndex]; + const compareResult = comparer(newItem, oldItem); + if (compareResult === Comparison.LessThan) { + inserted(newItem); + newIndex++; + } + else if (compareResult === Comparison.GreaterThan) { + deleted(oldItem); + oldIndex++; + } + else { + unchanged(oldItem, newItem); + newIndex++; + oldIndex++; + } + } + while (newIndex < newLen) { + inserted(newItems[newIndex++]); + } + while (oldIndex < oldLen) { + deleted(oldItems[oldIndex++]); + } +} diff --git a/src/emitter.ts b/src/emitter.ts new file mode 100644 index 0000000..bc7e9db --- /dev/null +++ b/src/emitter.ts @@ -0,0 +1,14 @@ +/** + * @file emiter + * @author meixuguang + */ + +import ts = require('typescript'); +import * as utilities from './utilities'; +import * as os from 'os'; + + +export function emitFile(sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker) { + const writer = utilities.createTextWriter(os.EOL); + writer.writeLine(); +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..96fded4 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,24 @@ +/** + * @file ts2php + * @author meixuguang + */ + +import * as ts from 'typescript'; +import path from 'path'; +import * as emitter from './emitter'; + +const program = ts.createProgram([path.resolve(__dirname, '../sample/index.ts')], { + target: ts.ScriptTarget.ES5, + module: ts.ModuleKind.CommonJS +}); + +const typeChecker = program.getTypeChecker(); + +for (const sourceFile of program.getSourceFiles()) { + if (!sourceFile.isDeclarationFile) { + ts.forEachChild(sourceFile, (node: ts.Node) => { + emitter.emitFile(node as ts.SourceFile, typeChecker); + }); + } +} + diff --git a/src/scanner.ts b/src/scanner.ts new file mode 100644 index 0000000..c54444a --- /dev/null +++ b/src/scanner.ts @@ -0,0 +1,51 @@ + +import { + CharacterCodes +} from './types'; + +export function computeLineStarts(text: string): number[] { + const result: number[] = new Array(); + let pos = 0; + let lineStart = 0; + while (pos < text.length) { + const ch = text.charCodeAt(pos); + pos++; + switch (ch) { + case CharacterCodes.carriageReturn: + if (text.charCodeAt(pos) === CharacterCodes.lineFeed) { + pos++; + } + // falls through + case CharacterCodes.lineFeed: + result.push(lineStart); + lineStart = pos; + break; + default: + if (ch > CharacterCodes.maxAsciiCharacter && isLineBreak(ch)) { + result.push(lineStart); + lineStart = pos; + } + break; + } + } + result.push(lineStart); + return result; +} + +export function isLineBreak(ch: number): boolean { + // ES5 7.3: + // The ECMAScript line terminator characters are listed in Table 3. + // Table 3: Line Terminator Characters + // Code Unit Value Name Formal Name + // \u000A Line Feed + // \u000D Carriage Return + // \u2028 Line separator + // \u2029 Paragraph separator + // Only the characters in Table 3 are treated as line terminators. Other new line or line + // breaking characters are treated as white space but not as line terminators. + + return ch === CharacterCodes.lineFeed || + ch === CharacterCodes.carriageReturn || + ch === CharacterCodes.lineSeparator || + ch === CharacterCodes.paragraphSeparator; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..d736c88 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,5239 @@ + +import { + Map, + ReadonlyMap, + MultiMap, + Pattern, + MapLike +} from './core'; + +import {SymbolFlags, SyntaxKind, Symbol, Node} from 'typescript'; + +// branded string type used to store absolute, normalized and canonicalized paths +// arbitrary file name can be converted to Path via toPath function +export type Path = string & { __pathBrand: any }; + +export interface TextRange { + pos: number; + end: number; +} + +export type JsDocSyntaxKind = + | SyntaxKind.EndOfFileToken + | SyntaxKind.WhitespaceTrivia + | SyntaxKind.AtToken + | SyntaxKind.NewLineTrivia + | SyntaxKind.AsteriskToken + | SyntaxKind.OpenBraceToken + | SyntaxKind.CloseBraceToken + | SyntaxKind.LessThanToken + | SyntaxKind.OpenBracketToken + | SyntaxKind.CloseBracketToken + | SyntaxKind.EqualsToken + | SyntaxKind.CommaToken + | SyntaxKind.DotToken + | SyntaxKind.Identifier + | SyntaxKind.NoSubstitutionTemplateLiteral + | SyntaxKind.Unknown + | KeywordSyntaxKind; + +export type KeywordSyntaxKind = + | SyntaxKind.AbstractKeyword + | SyntaxKind.AnyKeyword + | SyntaxKind.AsKeyword + | SyntaxKind.BooleanKeyword + | SyntaxKind.BreakKeyword + | SyntaxKind.CaseKeyword + | SyntaxKind.CatchKeyword + | SyntaxKind.ClassKeyword + | SyntaxKind.ContinueKeyword + | SyntaxKind.ConstKeyword + | SyntaxKind.ConstructorKeyword + | SyntaxKind.DebuggerKeyword + | SyntaxKind.DeclareKeyword + | SyntaxKind.DefaultKeyword + | SyntaxKind.DeleteKeyword + | SyntaxKind.DoKeyword + | SyntaxKind.ElseKeyword + | SyntaxKind.EnumKeyword + | SyntaxKind.ExportKeyword + | SyntaxKind.ExtendsKeyword + | SyntaxKind.FalseKeyword + | SyntaxKind.FinallyKeyword + | SyntaxKind.ForKeyword + | SyntaxKind.FromKeyword + | SyntaxKind.FunctionKeyword + | SyntaxKind.GetKeyword + | SyntaxKind.IfKeyword + | SyntaxKind.ImplementsKeyword + | SyntaxKind.ImportKeyword + | SyntaxKind.InKeyword + | SyntaxKind.InferKeyword + | SyntaxKind.InstanceOfKeyword + | SyntaxKind.InterfaceKeyword + | SyntaxKind.IsKeyword + | SyntaxKind.KeyOfKeyword + | SyntaxKind.LetKeyword + | SyntaxKind.ModuleKeyword + | SyntaxKind.NamespaceKeyword + | SyntaxKind.NeverKeyword + | SyntaxKind.NewKeyword + | SyntaxKind.NullKeyword + | SyntaxKind.NumberKeyword + | SyntaxKind.ObjectKeyword + | SyntaxKind.PackageKeyword + | SyntaxKind.PrivateKeyword + | SyntaxKind.ProtectedKeyword + | SyntaxKind.PublicKeyword + | SyntaxKind.ReadonlyKeyword + | SyntaxKind.RequireKeyword + | SyntaxKind.GlobalKeyword + | SyntaxKind.ReturnKeyword + | SyntaxKind.SetKeyword + | SyntaxKind.StaticKeyword + | SyntaxKind.StringKeyword + | SyntaxKind.SuperKeyword + | SyntaxKind.SwitchKeyword + | SyntaxKind.SymbolKeyword + | SyntaxKind.ThisKeyword + | SyntaxKind.ThrowKeyword + | SyntaxKind.TrueKeyword + | SyntaxKind.TryKeyword + | SyntaxKind.TypeKeyword + | SyntaxKind.TypeOfKeyword + | SyntaxKind.UndefinedKeyword + | SyntaxKind.UniqueKeyword + | SyntaxKind.UnknownKeyword + | SyntaxKind.VarKeyword + | SyntaxKind.VoidKeyword + | SyntaxKind.WhileKeyword + | SyntaxKind.WithKeyword + | SyntaxKind.YieldKeyword + | SyntaxKind.AsyncKeyword + | SyntaxKind.AwaitKeyword + | SyntaxKind.OfKeyword; + +export type JsxTokenSyntaxKind = + | SyntaxKind.LessThanSlashToken + | SyntaxKind.EndOfFileToken + | SyntaxKind.ConflictMarkerTrivia + | SyntaxKind.JsxText + | SyntaxKind.JsxTextAllWhiteSpaces + | SyntaxKind.OpenBraceToken + | SyntaxKind.LessThanToken; + +export const enum NodeFlags { + None = 0, + Let = 1 << 0, // Variable declaration + Const = 1 << 1, // Variable declaration + NestedNamespace = 1 << 2, // Namespace declaration + Synthesized = 1 << 3, // Node was synthesized during transformation + Namespace = 1 << 4, // Namespace declaration + ExportContext = 1 << 5, // Export context (initialized by binding) + ContainsThis = 1 << 6, // Interface contains references to "this" + HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding) + HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding) + GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope + HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding) + DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed + YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator + DecoratorContext = 1 << 13, // If node was parsed as part of a decorator + AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function + ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node + JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript + ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error + HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node + + // These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid + // walking the tree if the flags are not set. However, these flags are just a approximation + // (hence why it's named "PossiblyContainsDynamicImport") because once set, the flags never get cleared. + // During editing, if a dynamic import is removed, incremental parsing will *NOT* clear this flag. + // This means that the tree will always be traversed during module resolution, or when looking for external module indicators. + // However, the removal operation should not occur often and in the case of the + // removal, it is likely that users will add the import anyway. + // The advantage of this approach is its simplicity. For the case of batch compilation, + // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. + /* @internal */ PossiblyContainsDynamicImport = 1 << 19, + /* @internal */ PossiblyContainsImportMeta = 1 << 20, + + JSDoc = 1 << 21, // If node was parsed inside jsdoc + /* @internal */ Ambient = 1 << 22, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. + /* @internal */ InWithStatement = 1 << 23, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) + JsonFile = 1 << 24, // If node was parsed in a Json + + BlockScoped = Let | Const, + + ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn, + ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions, + + // Parsing context flags + ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement | Ambient, + + // Exclude these flags when parsing a Type + TypeExcludesFlags = YieldContext | AwaitContext, + + // Represents all flags that are potentially set once and + // never cleared on SourceFiles which get re-used in between incremental parses. + // See the comment above on `PossiblyContainsDynamicImport` and `PossiblyContainsImportMeta`. + /* @internal */ PermanentlySetIncrementalFlags = PossiblyContainsDynamicImport | PossiblyContainsImportMeta, +} + +export const enum ModifierFlags { + None = 0, + Export = 1 << 0, // Declarations + Ambient = 1 << 1, // Declarations + Public = 1 << 2, // Property/Method + Private = 1 << 3, // Property/Method + Protected = 1 << 4, // Property/Method + Static = 1 << 5, // Property/Method + Readonly = 1 << 6, // Property/Method + Abstract = 1 << 7, // Class/Method/ConstructSignature + Async = 1 << 8, // Property/Method/Function + Default = 1 << 9, // Function/Class (export default declaration) + Const = 1 << 11, // Const enum + HasComputedFlags = 1 << 29, // Modifier flags have been computed + + AccessibilityModifier = Public | Private | Protected, + // Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property. + ParameterPropertyModifier = AccessibilityModifier | Readonly, + NonPublicAccessibilityModifier = Private | Protected, + + TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const, + ExportDefault = Export | Default, + All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Async | Default | Const +} + +export const enum JsxFlags { + None = 0, + /** An element from a named property of the JSX.IntrinsicElements interface */ + IntrinsicNamedElement = 1 << 0, + /** An element inferred from the string index signature of the JSX.IntrinsicElements interface */ + IntrinsicIndexedElement = 1 << 1, + + IntrinsicElement = IntrinsicNamedElement | IntrinsicIndexedElement, +} + +/* @internal */ +export const enum RelationComparisonResult { + Succeeded = 1, // Should be truthy + Failed = 2, + FailedAndReported = 3 +} + +export interface JSDocContainer { + /* @internal */ jsDoc?: JSDoc[]; // JSDoc that directly precedes this node + /* @internal */ jsDocCache?: ReadonlyArray; // Cache for getJSDocTags +} + +export type HasJSDoc = + | ParameterDeclaration + | CallSignatureDeclaration + | ConstructSignatureDeclaration + | MethodSignature + | PropertySignature + | ArrowFunction + | ParenthesizedExpression + | SpreadAssignment + | ShorthandPropertyAssignment + | PropertyAssignment + | FunctionExpression + | LabeledStatement + | ExpressionStatement + | VariableStatement + | FunctionDeclaration + | ConstructorDeclaration + | MethodDeclaration + | PropertyDeclaration + | AccessorDeclaration + | ClassLikeDeclaration + | InterfaceDeclaration + | TypeAliasDeclaration + | EnumMember + | EnumDeclaration + | ModuleDeclaration + | ImportEqualsDeclaration + | IndexSignatureDeclaration + | FunctionTypeNode + | ConstructorTypeNode + | JSDocFunctionType + | ExportDeclaration + | EndOfFileToken; + +export type HasType = + | SignatureDeclaration + | VariableDeclaration + | ParameterDeclaration + | PropertySignature + | PropertyDeclaration + | TypePredicateNode + | ParenthesizedTypeNode + | TypeOperatorNode + | MappedTypeNode + | AssertionExpression + | TypeAliasDeclaration + | JSDocTypeExpression + | JSDocNonNullableType + | JSDocNullableType + | JSDocOptionalType + | JSDocVariadicType; + +export type HasInitializer = + | HasExpressionInitializer + | ForStatement + | ForInStatement + | ForOfStatement + | JsxAttribute; + +export type HasExpressionInitializer = + | VariableDeclaration + | ParameterDeclaration + | BindingElement + | PropertySignature + | PropertyDeclaration + | PropertyAssignment + | EnumMember; + +/* @internal */ +export type MutableNodeArray = NodeArray & T[]; + +export interface NodeArray extends ReadonlyArray, TextRange { + hasTrailingComma?: boolean; + /* @internal */ transformFlags: TransformFlags; // Flags for transforms, possibly undefined +} + +export interface Token extends Node { + kind: TKind; +} + +export type DotDotDotToken = Token; +export type QuestionToken = Token; +export type ExclamationToken = Token; +export type ColonToken = Token; +export type EqualsToken = Token; +export type AsteriskToken = Token; +export type EqualsGreaterThanToken = Token; +export type EndOfFileToken = Token & JSDocContainer; +export type AtToken = Token; +export type ReadonlyToken = Token; +export type AwaitKeywordToken = Token; +export type PlusToken = Token; +export type MinusToken = Token; + +export type Modifier + = Token + | Token + | Token + | Token + | Token + | Token + | Token + | Token + | Token + | Token + | Token + ; + +export type ModifiersArray = NodeArray; + +/*@internal*/ +export const enum GeneratedIdentifierFlags { + // Kinds + None = 0, // Not automatically generated. + Auto = 1, // Automatically generated identifier. + Loop = 2, // Automatically generated identifier with a preference for '_i'. + Unique = 3, // Unique name based on the 'text' property. + Node = 4, // Unique name based on the node in the 'original' property. + KindMask = 7, // Mask to extract the kind of identifier from its flags. + + // Flags + ReservedInNestedScopes = 1 << 3, // Reserve the generated name in nested scopes + Optimistic = 1 << 4, // First instance won't use '_#' if there's no conflict + FileLevel = 1 << 5, // Use only the file identifiers list and not generated names to search for conflicts +} + +export interface Identifier extends PrimaryExpression, Declaration { + kind: SyntaxKind.Identifier; + /** + * Prefer to use `id.unescapedText`. (Note: This is available only in services, not internally to the TypeScript compiler.) + * Text of identifier, but if the identifier begins with two underscores, this will begin with three. + */ + escapedText: __String; + originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later + /*@internal*/ autoGenerateFlags?: GeneratedIdentifierFlags; // Specifies whether to auto-generate the text for an identifier. + /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. + isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace + /*@internal*/ typeArguments?: NodeArray; // Only defined on synthesized nodes. Though not syntactically valid, used in emitting diagnostics, quickinfo, and signature help. + /*@internal*/ jsdocDotPos?: number; // Identifier occurs in JSDoc-style generic: Id. +} + +// Transient identifier node (marked by id === -1) +export interface TransientIdentifier extends Identifier { + resolvedSymbol: Symbol; +} + +/*@internal*/ +export interface GeneratedIdentifier extends Identifier { + autoGenerateFlags: GeneratedIdentifierFlags; +} + +export interface QualifiedName extends Node { + kind: SyntaxKind.QualifiedName; + left: EntityName; + right: Identifier; + /*@internal*/ jsdocDotPos?: number; // QualifiedName occurs in JSDoc-style generic: Id1.Id2. +} + +export type EntityName = Identifier | QualifiedName; + +export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName; + +export type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | BindingPattern; + +export interface Declaration extends Node { + _declarationBrand: any; +} + +export interface NamedDeclaration extends Declaration { + name?: DeclarationName; +} + +/* @internal */ +export interface DynamicNamedDeclaration extends NamedDeclaration { + name: ComputedPropertyName; +} + +/* @internal */ +// A declaration that supports late-binding (used in checker) +export interface LateBoundDeclaration extends DynamicNamedDeclaration { + name: LateBoundName; +} + +export interface DeclarationStatement extends NamedDeclaration, Statement { + name?: Identifier | StringLiteral | NumericLiteral; +} + +export interface ComputedPropertyName extends Node { + parent: Declaration; + kind: SyntaxKind.ComputedPropertyName; + expression: Expression; +} + +/* @internal */ +// A name that supports late-binding (used in checker) +export interface LateBoundName extends ComputedPropertyName { + expression: EntityNameExpression; +} + +export interface Decorator extends Node { + kind: SyntaxKind.Decorator; + parent: NamedDeclaration; + expression: LeftHandSideExpression; +} + +export interface TypeParameterDeclaration extends NamedDeclaration { + kind: SyntaxKind.TypeParameter; + parent: DeclarationWithTypeParameterChildren | InferTypeNode; + name: Identifier; + /** Note: Consider calling `getEffectiveConstraintOfTypeParameter` */ + constraint?: TypeNode; + default?: TypeNode; + + // For error recovery purposes. + expression?: Expression; +} + +export interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer { + kind: SignatureDeclaration["kind"]; + name?: PropertyName; + typeParameters?: NodeArray; + parameters: NodeArray; + type?: TypeNode; + /* @internal */ typeArguments?: NodeArray; // Used for quick info, replaces typeParameters for instantiated signatures +} + +export type SignatureDeclaration = + | CallSignatureDeclaration + | ConstructSignatureDeclaration + | MethodSignature + | IndexSignatureDeclaration + | FunctionTypeNode + | ConstructorTypeNode + | JSDocFunctionType + | FunctionDeclaration + | MethodDeclaration + | ConstructorDeclaration + | AccessorDeclaration + | FunctionExpression + | ArrowFunction; + +export interface CallSignatureDeclaration extends SignatureDeclarationBase, TypeElement { + kind: SyntaxKind.CallSignature; +} + +export interface ConstructSignatureDeclaration extends SignatureDeclarationBase, TypeElement { + kind: SyntaxKind.ConstructSignature; +} + +export type BindingName = Identifier | BindingPattern; + +export interface VariableDeclaration extends NamedDeclaration { + kind: SyntaxKind.VariableDeclaration; + parent: VariableDeclarationList | CatchClause; + name: BindingName; // Declared variable name + exclamationToken?: ExclamationToken; // Optional definite assignment assertion + type?: TypeNode; // Optional type annotation + initializer?: Expression; // Optional initializer +} + +export interface VariableDeclarationList extends Node { + kind: SyntaxKind.VariableDeclarationList; + parent: VariableStatement | ForStatement | ForOfStatement | ForInStatement; + declarations: NodeArray; +} + +export interface ParameterDeclaration extends NamedDeclaration, JSDocContainer { + kind: SyntaxKind.Parameter; + parent: SignatureDeclaration; + dotDotDotToken?: DotDotDotToken; // Present on rest parameter + name: BindingName; // Declared parameter name. + questionToken?: QuestionToken; // Present on optional parameter + type?: TypeNode; // Optional type annotation + initializer?: Expression; // Optional initializer +} + +export interface BindingElement extends NamedDeclaration { + kind: SyntaxKind.BindingElement; + parent: BindingPattern; + propertyName?: PropertyName; // Binding property name (in object binding pattern) + dotDotDotToken?: DotDotDotToken; // Present on rest element (in object binding pattern) + name: BindingName; // Declared binding element name + initializer?: Expression; // Optional initializer +} + +/*@internal*/ +export type BindingElementGrandparent = BindingElement["parent"]["parent"]; + +export interface PropertySignature extends TypeElement, JSDocContainer { + kind: SyntaxKind.PropertySignature; + name: PropertyName; // Declared property name + questionToken?: QuestionToken; // Present on optional property + type?: TypeNode; // Optional type annotation + initializer?: Expression; // Optional initializer +} + +export interface PropertyDeclaration extends ClassElement, JSDocContainer { + kind: SyntaxKind.PropertyDeclaration; + parent: ClassLikeDeclaration; + name: PropertyName; + questionToken?: QuestionToken; // Present for use with reporting a grammar error + exclamationToken?: ExclamationToken; + type?: TypeNode; + initializer?: Expression; // Optional initializer +} + +export interface ObjectLiteralElement extends NamedDeclaration { + _objectLiteralBrandBrand: any; + name?: PropertyName; +} + +/** Unlike ObjectLiteralElement, excludes JSXAttribute and JSXSpreadAttribute. */ +export type ObjectLiteralElementLike + = PropertyAssignment + | ShorthandPropertyAssignment + | SpreadAssignment + | MethodDeclaration + | AccessorDeclaration + ; + +export interface PropertyAssignment extends ObjectLiteralElement, JSDocContainer { + parent: ObjectLiteralExpression; + kind: SyntaxKind.PropertyAssignment; + name: PropertyName; + questionToken?: QuestionToken; + initializer: Expression; +} + +export interface ShorthandPropertyAssignment extends ObjectLiteralElement, JSDocContainer { + parent: ObjectLiteralExpression; + kind: SyntaxKind.ShorthandPropertyAssignment; + name: Identifier; + questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; + // used when ObjectLiteralExpression is used in ObjectAssignmentPattern + // it is grammar error to appear in actual object initializer + equalsToken?: Token; + objectAssignmentInitializer?: Expression; +} + +export interface SpreadAssignment extends ObjectLiteralElement, JSDocContainer { + parent: ObjectLiteralExpression; + kind: SyntaxKind.SpreadAssignment; + expression: Expression; +} + +export type VariableLikeDeclaration = + | VariableDeclaration + | ParameterDeclaration + | BindingElement + | PropertyDeclaration + | PropertyAssignment + | PropertySignature + | JsxAttribute + | ShorthandPropertyAssignment + | EnumMember + | JSDocPropertyTag + | JSDocParameterTag; + +export interface PropertyLikeDeclaration extends NamedDeclaration { + name: PropertyName; +} + +export interface ObjectBindingPattern extends Node { + kind: SyntaxKind.ObjectBindingPattern; + parent: VariableDeclaration | ParameterDeclaration | BindingElement; + elements: NodeArray; +} + +export interface ArrayBindingPattern extends Node { + kind: SyntaxKind.ArrayBindingPattern; + parent: VariableDeclaration | ParameterDeclaration | BindingElement; + elements: NodeArray; +} + +export type BindingPattern = ObjectBindingPattern | ArrayBindingPattern; + +export type ArrayBindingElement = BindingElement | OmittedExpression; + +/** + * Several node kinds share function-like features such as a signature, + * a name, and a body. These nodes should extend FunctionLikeDeclarationBase. + * Examples: + * - FunctionDeclaration + * - MethodDeclaration + * - AccessorDeclaration + */ +export interface FunctionLikeDeclarationBase extends SignatureDeclarationBase { + _functionLikeDeclarationBrand: any; + + asteriskToken?: AsteriskToken; + questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; + body?: Block | Expression; +} + +export type FunctionLikeDeclaration = + | FunctionDeclaration + | MethodDeclaration + | GetAccessorDeclaration + | SetAccessorDeclaration + | ConstructorDeclaration + | FunctionExpression + | ArrowFunction; +/** @deprecated Use SignatureDeclaration */ +export type FunctionLike = SignatureDeclaration; + +export interface FunctionDeclaration extends FunctionLikeDeclarationBase, DeclarationStatement { + kind: SyntaxKind.FunctionDeclaration; + name?: Identifier; + body?: FunctionBody; +} + +export interface MethodSignature extends SignatureDeclarationBase, TypeElement { + kind: SyntaxKind.MethodSignature; + parent: ObjectTypeDeclaration; + name: PropertyName; +} + +// Note that a MethodDeclaration is considered both a ClassElement and an ObjectLiteralElement. +// Both the grammars for ClassDeclaration and ObjectLiteralExpression allow for MethodDeclarations +// as child elements, and so a MethodDeclaration satisfies both interfaces. This avoids the +// alternative where we would need separate kinds/types for ClassMethodDeclaration and +// ObjectLiteralMethodDeclaration, which would look identical. +// +// Because of this, it may be necessary to determine what sort of MethodDeclaration you have +// at later stages of the compiler pipeline. In that case, you can either check the parent kind +// of the method, or use helpers like isObjectLiteralMethodDeclaration +export interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer { + kind: SyntaxKind.MethodDeclaration; + parent: ClassLikeDeclaration | ObjectLiteralExpression; + name: PropertyName; + body?: FunctionBody; +} + +export interface ConstructorDeclaration extends FunctionLikeDeclarationBase, ClassElement, JSDocContainer { + kind: SyntaxKind.Constructor; + parent: ClassLikeDeclaration; + body?: FunctionBody; + /* @internal */ returnFlowNode?: FlowNode; +} + +/** For when we encounter a semicolon in a class declaration. ES6 allows these as class elements. */ +export interface SemicolonClassElement extends ClassElement { + kind: SyntaxKind.SemicolonClassElement; + parent: ClassLikeDeclaration; +} + +// See the comment on MethodDeclaration for the intuition behind GetAccessorDeclaration being a +// ClassElement and an ObjectLiteralElement. +export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer { + kind: SyntaxKind.GetAccessor; + parent: ClassLikeDeclaration | ObjectLiteralExpression; + name: PropertyName; + body?: FunctionBody; +} + +// See the comment on MethodDeclaration for the intuition behind SetAccessorDeclaration being a +// ClassElement and an ObjectLiteralElement. +export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer { + kind: SyntaxKind.SetAccessor; + parent: ClassLikeDeclaration | ObjectLiteralExpression; + name: PropertyName; + body?: FunctionBody; +} + +export type AccessorDeclaration = GetAccessorDeclaration | SetAccessorDeclaration; + +export interface IndexSignatureDeclaration extends SignatureDeclarationBase, ClassElement, TypeElement { + kind: SyntaxKind.IndexSignature; + parent: ObjectTypeDeclaration; +} + +export interface TypeNode extends Node { + _typeNodeBrand: any; +} + +export interface KeywordTypeNode extends TypeNode { + kind: SyntaxKind.AnyKeyword + | SyntaxKind.UnknownKeyword + | SyntaxKind.NumberKeyword + | SyntaxKind.ObjectKeyword + | SyntaxKind.BooleanKeyword + | SyntaxKind.StringKeyword + | SyntaxKind.SymbolKeyword + | SyntaxKind.ThisKeyword + | SyntaxKind.VoidKeyword + | SyntaxKind.UndefinedKeyword + | SyntaxKind.NullKeyword + | SyntaxKind.NeverKeyword; +} + +export interface ImportTypeNode extends NodeWithTypeArguments { + kind: SyntaxKind.ImportType; + isTypeOf?: boolean; + argument: TypeNode; + qualifier?: EntityName; +} + +/* @internal */ +export type LiteralImportTypeNode = ImportTypeNode & { argument: LiteralTypeNode & { literal: StringLiteral } }; + +export interface ThisTypeNode extends TypeNode { + kind: SyntaxKind.ThisType; +} + +export type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode; + +export interface FunctionOrConstructorTypeNodeBase extends TypeNode, SignatureDeclarationBase { + kind: SyntaxKind.FunctionType | SyntaxKind.ConstructorType; + type: TypeNode; +} + +export interface FunctionTypeNode extends FunctionOrConstructorTypeNodeBase { + kind: SyntaxKind.FunctionType; +} + +export interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase { + kind: SyntaxKind.ConstructorType; +} + +export interface NodeWithTypeArguments extends TypeNode { + typeArguments?: NodeArray; +} + +export type TypeReferenceType = TypeReferenceNode | ExpressionWithTypeArguments; + +export interface TypeReferenceNode extends NodeWithTypeArguments { + kind: SyntaxKind.TypeReference; + typeName: EntityName; +} + +export interface TypePredicateNode extends TypeNode { + kind: SyntaxKind.TypePredicate; + parent: SignatureDeclaration | JSDocTypeExpression; + parameterName: Identifier | ThisTypeNode; + type: TypeNode; +} + +export interface TypeQueryNode extends TypeNode { + kind: SyntaxKind.TypeQuery; + exprName: EntityName; +} + +// A TypeLiteral is the declaration node for an anonymous symbol. +export interface TypeLiteralNode extends TypeNode, Declaration { + kind: SyntaxKind.TypeLiteral; + members: NodeArray; +} + +export interface ArrayTypeNode extends TypeNode { + kind: SyntaxKind.ArrayType; + elementType: TypeNode; +} + +export interface TupleTypeNode extends TypeNode { + kind: SyntaxKind.TupleType; + elementTypes: NodeArray; +} + +export interface OptionalTypeNode extends TypeNode { + kind: SyntaxKind.OptionalType; + type: TypeNode; +} + +export interface RestTypeNode extends TypeNode { + kind: SyntaxKind.RestType; + type: TypeNode; +} + +export type UnionOrIntersectionTypeNode = UnionTypeNode | IntersectionTypeNode; + +export interface UnionTypeNode extends TypeNode { + kind: SyntaxKind.UnionType; + types: NodeArray; +} + +export interface IntersectionTypeNode extends TypeNode { + kind: SyntaxKind.IntersectionType; + types: NodeArray; +} + +export interface ConditionalTypeNode extends TypeNode { + kind: SyntaxKind.ConditionalType; + checkType: TypeNode; + extendsType: TypeNode; + trueType: TypeNode; + falseType: TypeNode; +} + +export interface InferTypeNode extends TypeNode { + kind: SyntaxKind.InferType; + typeParameter: TypeParameterDeclaration; +} + +export interface ParenthesizedTypeNode extends TypeNode { + kind: SyntaxKind.ParenthesizedType; + type: TypeNode; +} + +export interface TypeOperatorNode extends TypeNode { + kind: SyntaxKind.TypeOperator; + operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword; + type: TypeNode; +} + +/* @internal */ +export interface UniqueTypeOperatorNode extends TypeOperatorNode { + operator: SyntaxKind.UniqueKeyword; +} + +export interface IndexedAccessTypeNode extends TypeNode { + kind: SyntaxKind.IndexedAccessType; + objectType: TypeNode; + indexType: TypeNode; +} + +export interface MappedTypeNode extends TypeNode, Declaration { + kind: SyntaxKind.MappedType; + readonlyToken?: ReadonlyToken | PlusToken | MinusToken; + typeParameter: TypeParameterDeclaration; + questionToken?: QuestionToken | PlusToken | MinusToken; + type?: TypeNode; +} + +export interface LiteralTypeNode extends TypeNode { + kind: SyntaxKind.LiteralType; + literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression; +} + +export interface StringLiteral extends LiteralExpression { + kind: SyntaxKind.StringLiteral; + /* @internal */ textSourceNode?: Identifier | StringLiteralLike | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms). + /** Note: this is only set when synthesizing a node, not during parsing. */ + /* @internal */ singleQuote?: boolean; +} + +export type StringLiteralLike = StringLiteral | NoSubstitutionTemplateLiteral; + +// Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing. +// Consider 'Expression'. Without the brand, 'Expression' is actually no different +// (structurally) than 'Node'. Because of this you can pass any Node to a function that +// takes an Expression without any error. By using the 'brands' we ensure that the type +// checker actually thinks you have something of the right type. Note: the brands are +// never actually given values. At runtime they have zero cost. + +export interface Expression extends Node { + _expressionBrand: any; +} + +export interface OmittedExpression extends Expression { + kind: SyntaxKind.OmittedExpression; +} + +// Represents an expression that is elided as part of a transformation to emit comments on a +// not-emitted node. The 'expression' property of a PartiallyEmittedExpression should be emitted. +export interface PartiallyEmittedExpression extends LeftHandSideExpression { + kind: SyntaxKind.PartiallyEmittedExpression; + expression: Expression; +} + +export interface UnaryExpression extends Expression { + _unaryExpressionBrand: any; +} + +/** Deprecated, please use UpdateExpression */ +export type IncrementExpression = UpdateExpression; +export interface UpdateExpression extends UnaryExpression { + _updateExpressionBrand: any; +} + +// see: https://tc39.github.io/ecma262/#prod-UpdateExpression +// see: https://tc39.github.io/ecma262/#prod-UnaryExpression +export type PrefixUnaryOperator + = SyntaxKind.PlusPlusToken + | SyntaxKind.MinusMinusToken + | SyntaxKind.PlusToken + | SyntaxKind.MinusToken + | SyntaxKind.TildeToken + | SyntaxKind.ExclamationToken; + +export interface PrefixUnaryExpression extends UpdateExpression { + kind: SyntaxKind.PrefixUnaryExpression; + operator: PrefixUnaryOperator; + operand: UnaryExpression; +} + +// see: https://tc39.github.io/ecma262/#prod-UpdateExpression +export type PostfixUnaryOperator + = SyntaxKind.PlusPlusToken + | SyntaxKind.MinusMinusToken + ; + +export interface PostfixUnaryExpression extends UpdateExpression { + kind: SyntaxKind.PostfixUnaryExpression; + operand: LeftHandSideExpression; + operator: PostfixUnaryOperator; +} + +export interface LeftHandSideExpression extends UpdateExpression { + _leftHandSideExpressionBrand: any; +} + +export interface MemberExpression extends LeftHandSideExpression { + _memberExpressionBrand: any; +} + +export interface PrimaryExpression extends MemberExpression { + _primaryExpressionBrand: any; +} + +export interface NullLiteral extends PrimaryExpression, TypeNode { + kind: SyntaxKind.NullKeyword; +} + +export interface BooleanLiteral extends PrimaryExpression, TypeNode { + kind: SyntaxKind.TrueKeyword | SyntaxKind.FalseKeyword; +} + +export interface ThisExpression extends PrimaryExpression, KeywordTypeNode { + kind: SyntaxKind.ThisKeyword; +} + +export interface SuperExpression extends PrimaryExpression { + kind: SyntaxKind.SuperKeyword; +} + +export interface ImportExpression extends PrimaryExpression { + kind: SyntaxKind.ImportKeyword; +} + +export interface DeleteExpression extends UnaryExpression { + kind: SyntaxKind.DeleteExpression; + expression: UnaryExpression; +} + +export interface TypeOfExpression extends UnaryExpression { + kind: SyntaxKind.TypeOfExpression; + expression: UnaryExpression; +} + +export interface VoidExpression extends UnaryExpression { + kind: SyntaxKind.VoidExpression; + expression: UnaryExpression; +} + +export interface AwaitExpression extends UnaryExpression { + kind: SyntaxKind.AwaitExpression; + expression: UnaryExpression; +} + +export interface YieldExpression extends Expression { + kind: SyntaxKind.YieldExpression; + asteriskToken?: AsteriskToken; + expression?: Expression; +} + +export interface SyntheticExpression extends Expression { + kind: SyntaxKind.SyntheticExpression; + isSpread: boolean; + type: Type; +} + +// see: https://tc39.github.io/ecma262/#prod-ExponentiationExpression +export type ExponentiationOperator + = SyntaxKind.AsteriskAsteriskToken + ; + +// see: https://tc39.github.io/ecma262/#prod-MultiplicativeOperator +export type MultiplicativeOperator + = SyntaxKind.AsteriskToken + | SyntaxKind.SlashToken + | SyntaxKind.PercentToken + ; + +// see: https://tc39.github.io/ecma262/#prod-MultiplicativeExpression +export type MultiplicativeOperatorOrHigher + = ExponentiationOperator + | MultiplicativeOperator + ; + +// see: https://tc39.github.io/ecma262/#prod-AdditiveExpression +export type AdditiveOperator + = SyntaxKind.PlusToken + | SyntaxKind.MinusToken + ; + +// see: https://tc39.github.io/ecma262/#prod-AdditiveExpression +export type AdditiveOperatorOrHigher + = MultiplicativeOperatorOrHigher + | AdditiveOperator + ; + +// see: https://tc39.github.io/ecma262/#prod-ShiftExpression +export type ShiftOperator + = SyntaxKind.LessThanLessThanToken + | SyntaxKind.GreaterThanGreaterThanToken + | SyntaxKind.GreaterThanGreaterThanGreaterThanToken + ; + +// see: https://tc39.github.io/ecma262/#prod-ShiftExpression +export type ShiftOperatorOrHigher + = AdditiveOperatorOrHigher + | ShiftOperator + ; + +// see: https://tc39.github.io/ecma262/#prod-RelationalExpression +export type RelationalOperator + = SyntaxKind.LessThanToken + | SyntaxKind.LessThanEqualsToken + | SyntaxKind.GreaterThanToken + | SyntaxKind.GreaterThanEqualsToken + | SyntaxKind.InstanceOfKeyword + | SyntaxKind.InKeyword + ; + +// see: https://tc39.github.io/ecma262/#prod-RelationalExpression +export type RelationalOperatorOrHigher + = ShiftOperatorOrHigher + | RelationalOperator + ; + +// see: https://tc39.github.io/ecma262/#prod-EqualityExpression +export type EqualityOperator + = SyntaxKind.EqualsEqualsToken + | SyntaxKind.EqualsEqualsEqualsToken + | SyntaxKind.ExclamationEqualsEqualsToken + | SyntaxKind.ExclamationEqualsToken + ; + +// see: https://tc39.github.io/ecma262/#prod-EqualityExpression +export type EqualityOperatorOrHigher + = RelationalOperatorOrHigher + | EqualityOperator; + +// see: https://tc39.github.io/ecma262/#prod-BitwiseANDExpression +// see: https://tc39.github.io/ecma262/#prod-BitwiseXORExpression +// see: https://tc39.github.io/ecma262/#prod-BitwiseORExpression +export type BitwiseOperator + = SyntaxKind.AmpersandToken + | SyntaxKind.BarToken + | SyntaxKind.CaretToken + ; + +// see: https://tc39.github.io/ecma262/#prod-BitwiseANDExpression +// see: https://tc39.github.io/ecma262/#prod-BitwiseXORExpression +// see: https://tc39.github.io/ecma262/#prod-BitwiseORExpression +export type BitwiseOperatorOrHigher + = EqualityOperatorOrHigher + | BitwiseOperator + ; + +// see: https://tc39.github.io/ecma262/#prod-LogicalANDExpression +// see: https://tc39.github.io/ecma262/#prod-LogicalORExpression +export type LogicalOperator + = SyntaxKind.AmpersandAmpersandToken + | SyntaxKind.BarBarToken + ; + +// see: https://tc39.github.io/ecma262/#prod-LogicalANDExpression +// see: https://tc39.github.io/ecma262/#prod-LogicalORExpression +export type LogicalOperatorOrHigher + = BitwiseOperatorOrHigher + | LogicalOperator + ; + +// see: https://tc39.github.io/ecma262/#prod-AssignmentOperator +export type CompoundAssignmentOperator + = SyntaxKind.PlusEqualsToken + | SyntaxKind.MinusEqualsToken + | SyntaxKind.AsteriskAsteriskEqualsToken + | SyntaxKind.AsteriskEqualsToken + | SyntaxKind.SlashEqualsToken + | SyntaxKind.PercentEqualsToken + | SyntaxKind.AmpersandEqualsToken + | SyntaxKind.BarEqualsToken + | SyntaxKind.CaretEqualsToken + | SyntaxKind.LessThanLessThanEqualsToken + | SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken + | SyntaxKind.GreaterThanGreaterThanEqualsToken + ; + +// see: https://tc39.github.io/ecma262/#prod-AssignmentExpression +export type AssignmentOperator + = SyntaxKind.EqualsToken + | CompoundAssignmentOperator + ; + +// see: https://tc39.github.io/ecma262/#prod-AssignmentExpression +export type AssignmentOperatorOrHigher + = LogicalOperatorOrHigher + | AssignmentOperator + ; + +// see: https://tc39.github.io/ecma262/#prod-Expression +export type BinaryOperator + = AssignmentOperatorOrHigher + | SyntaxKind.CommaToken + ; + +export type BinaryOperatorToken = Token; + +export interface BinaryExpression extends Expression, Declaration { + kind: SyntaxKind.BinaryExpression; + left: Expression; + operatorToken: BinaryOperatorToken; + right: Expression; +} + +export type AssignmentOperatorToken = Token; + +export interface AssignmentExpression extends BinaryExpression { + left: LeftHandSideExpression; + operatorToken: TOperator; +} + +export interface ObjectDestructuringAssignment extends AssignmentExpression { + left: ObjectLiteralExpression; +} + +export interface ArrayDestructuringAssignment extends AssignmentExpression { + left: ArrayLiteralExpression; +} + +export type DestructuringAssignment + = ObjectDestructuringAssignment + | ArrayDestructuringAssignment + ; + +export type BindingOrAssignmentElement + = VariableDeclaration + | ParameterDeclaration + | BindingElement + | PropertyAssignment // AssignmentProperty + | ShorthandPropertyAssignment // AssignmentProperty + | SpreadAssignment // AssignmentRestProperty + | OmittedExpression // Elision + | SpreadElement // AssignmentRestElement + | ArrayLiteralExpression // ArrayAssignmentPattern + | ObjectLiteralExpression // ObjectAssignmentPattern + | AssignmentExpression // AssignmentElement + | Identifier // DestructuringAssignmentTarget + | PropertyAccessExpression // DestructuringAssignmentTarget + | ElementAccessExpression // DestructuringAssignmentTarget + ; + +export type BindingOrAssignmentElementRestIndicator + = DotDotDotToken // from BindingElement + | SpreadElement // AssignmentRestElement + | SpreadAssignment // AssignmentRestProperty + ; + +export type BindingOrAssignmentElementTarget = BindingOrAssignmentPattern | Identifier | PropertyAccessExpression | ElementAccessExpression | OmittedExpression; + +export type ObjectBindingOrAssignmentPattern + = ObjectBindingPattern + | ObjectLiteralExpression // ObjectAssignmentPattern + ; + +export type ArrayBindingOrAssignmentPattern + = ArrayBindingPattern + | ArrayLiteralExpression // ArrayAssignmentPattern + ; + +export type AssignmentPattern = ObjectLiteralExpression | ArrayLiteralExpression; + +export type BindingOrAssignmentPattern = ObjectBindingOrAssignmentPattern | ArrayBindingOrAssignmentPattern; + +export interface ConditionalExpression extends Expression { + kind: SyntaxKind.ConditionalExpression; + condition: Expression; + questionToken: QuestionToken; + whenTrue: Expression; + colonToken: ColonToken; + whenFalse: Expression; +} + +export type FunctionBody = Block; +export type ConciseBody = FunctionBody | Expression; + +export interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclarationBase, JSDocContainer { + kind: SyntaxKind.FunctionExpression; + name?: Identifier; + body: FunctionBody; // Required, whereas the member inherited from FunctionDeclaration is optional +} + +export interface ArrowFunction extends Expression, FunctionLikeDeclarationBase, JSDocContainer { + kind: SyntaxKind.ArrowFunction; + equalsGreaterThanToken: EqualsGreaterThanToken; + body: ConciseBody; + name: never; +} + +// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral, +// or any literal of a template, this means quotes have been removed and escapes have been converted to actual characters. +// For a NumericLiteral, the stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1". +export interface LiteralLikeNode extends Node { + text: string; + isUnterminated?: boolean; + hasExtendedUnicodeEscape?: boolean; +} + +// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral, +// or any literal of a template, this means quotes have been removed and escapes have been converted to actual characters. +// For a NumericLiteral, the stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1". +export interface LiteralExpression extends LiteralLikeNode, PrimaryExpression { + _literalExpressionBrand: any; +} + +export interface RegularExpressionLiteral extends LiteralExpression { + kind: SyntaxKind.RegularExpressionLiteral; +} + +export interface NoSubstitutionTemplateLiteral extends LiteralExpression { + kind: SyntaxKind.NoSubstitutionTemplateLiteral; +} + +/* @internal */ +export const enum TokenFlags { + None = 0, + PrecedingLineBreak = 1 << 0, + PrecedingJSDocComment = 1 << 1, + Unterminated = 1 << 2, + ExtendedUnicodeEscape = 1 << 3, + Scientific = 1 << 4, // e.g. `10e2` + Octal = 1 << 5, // e.g. `0777` + HexSpecifier = 1 << 6, // e.g. `0x00000000` + BinarySpecifier = 1 << 7, // e.g. `0b0110010000000000` + OctalSpecifier = 1 << 8, // e.g. `0o777` + ContainsSeparator = 1 << 9, // e.g. `0b1100_0101` + BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier, + NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinarySpecifier | OctalSpecifier | ContainsSeparator +} + +export interface NumericLiteral extends LiteralExpression { + kind: SyntaxKind.NumericLiteral; + /* @internal */ + numericLiteralFlags: TokenFlags; +} + +export interface TemplateHead extends LiteralLikeNode { + kind: SyntaxKind.TemplateHead; + parent: TemplateExpression; +} + +export interface TemplateMiddle extends LiteralLikeNode { + kind: SyntaxKind.TemplateMiddle; + parent: TemplateSpan; +} + +export interface TemplateTail extends LiteralLikeNode { + kind: SyntaxKind.TemplateTail; + parent: TemplateSpan; +} + +export type TemplateLiteral = TemplateExpression | NoSubstitutionTemplateLiteral; + +export interface TemplateExpression extends PrimaryExpression { + kind: SyntaxKind.TemplateExpression; + head: TemplateHead; + templateSpans: NodeArray; +} + +// Each of these corresponds to a substitution expression and a template literal, in that order. +// The template literal must have kind TemplateMiddleLiteral or TemplateTailLiteral. +export interface TemplateSpan extends Node { + kind: SyntaxKind.TemplateSpan; + parent: TemplateExpression; + expression: Expression; + literal: TemplateMiddle | TemplateTail; +} + +export interface ParenthesizedExpression extends PrimaryExpression, JSDocContainer { + kind: SyntaxKind.ParenthesizedExpression; + expression: Expression; +} + +export interface ArrayLiteralExpression extends PrimaryExpression { + kind: SyntaxKind.ArrayLiteralExpression; + elements: NodeArray; + /* @internal */ + multiLine?: boolean; +} + +export interface SpreadElement extends Expression { + kind: SyntaxKind.SpreadElement; + parent: ArrayLiteralExpression | CallExpression | NewExpression; + expression: Expression; +} + +/** + * This interface is a base interface for ObjectLiteralExpression and JSXAttributes to extend from. JSXAttributes is similar to + * ObjectLiteralExpression in that it contains array of properties; however, JSXAttributes' properties can only be + * JSXAttribute or JSXSpreadAttribute. ObjectLiteralExpression, on the other hand, can only have properties of type + * ObjectLiteralElement (e.g. PropertyAssignment, ShorthandPropertyAssignment etc.) + */ +export interface ObjectLiteralExpressionBase extends PrimaryExpression, Declaration { + properties: NodeArray; +} + +// An ObjectLiteralExpression is the declaration node for an anonymous symbol. +export interface ObjectLiteralExpression extends ObjectLiteralExpressionBase { + kind: SyntaxKind.ObjectLiteralExpression; + /* @internal */ + multiLine?: boolean; +} + +export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression; +export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression; + +export interface PropertyAccessExpression extends MemberExpression, NamedDeclaration { + kind: SyntaxKind.PropertyAccessExpression; + expression: LeftHandSideExpression; + name: Identifier; +} + +export interface SuperPropertyAccessExpression extends PropertyAccessExpression { + expression: SuperExpression; +} + +/** Brand for a PropertyAccessExpression which, like a QualifiedName, consists of a sequence of identifiers separated by dots. */ +export interface PropertyAccessEntityNameExpression extends PropertyAccessExpression { + _propertyAccessExpressionLikeQualifiedNameBrand?: any; + expression: EntityNameExpression; +} + +export interface ElementAccessExpression extends MemberExpression { + kind: SyntaxKind.ElementAccessExpression; + expression: LeftHandSideExpression; + argumentExpression: Expression; +} + +export interface SuperElementAccessExpression extends ElementAccessExpression { + expression: SuperExpression; +} + +// see: https://tc39.github.io/ecma262/#prod-SuperProperty +export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; + +export interface CallExpression extends LeftHandSideExpression, Declaration { + kind: SyntaxKind.CallExpression; + expression: LeftHandSideExpression; + typeArguments?: NodeArray; + arguments: NodeArray; +} + +/** @internal */ +export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: EntityNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } }; + +// see: https://tc39.github.io/ecma262/#prod-SuperCall +export interface SuperCall extends CallExpression { + expression: SuperExpression; +} + +export interface ImportCall extends CallExpression { + expression: ImportExpression; +} + +export interface ExpressionWithTypeArguments extends NodeWithTypeArguments { + kind: SyntaxKind.ExpressionWithTypeArguments; + parent: HeritageClause | JSDocAugmentsTag; + expression: LeftHandSideExpression; +} + +export interface NewExpression extends PrimaryExpression, Declaration { + kind: SyntaxKind.NewExpression; + expression: LeftHandSideExpression; + typeArguments?: NodeArray; + arguments?: NodeArray; +} + +export interface TaggedTemplateExpression extends MemberExpression { + kind: SyntaxKind.TaggedTemplateExpression; + tag: LeftHandSideExpression; + typeArguments?: NodeArray; + template: TemplateLiteral; +} + +export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement; + +export interface AsExpression extends Expression { + kind: SyntaxKind.AsExpression; + expression: Expression; + type: TypeNode; +} + +export interface TypeAssertion extends UnaryExpression { + kind: SyntaxKind.TypeAssertionExpression; + type: TypeNode; + expression: UnaryExpression; +} + +export type AssertionExpression = TypeAssertion | AsExpression; + +export interface NonNullExpression extends LeftHandSideExpression { + kind: SyntaxKind.NonNullExpression; + expression: Expression; +} + +// NOTE: MetaProperty is really a MemberExpression, but we consider it a PrimaryExpression +// for the same reasons we treat NewExpression as a PrimaryExpression. +export interface MetaProperty extends PrimaryExpression { + kind: SyntaxKind.MetaProperty; + keywordToken: SyntaxKind.NewKeyword | SyntaxKind.ImportKeyword; + name: Identifier; +} + +/// A JSX expression of the form ... +export interface JsxElement extends PrimaryExpression { + kind: SyntaxKind.JsxElement; + openingElement: JsxOpeningElement; + children: NodeArray; + closingElement: JsxClosingElement; +} + +/// Either the opening tag in a ... pair or the lone in a self-closing form +export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement; + +export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute; + +export type JsxTagNameExpression = Identifier | ThisExpression | JsxTagNamePropertyAccess; + +export interface JsxTagNamePropertyAccess extends PropertyAccessExpression { + expression: JsxTagNameExpression; +} + +export interface JsxAttributes extends ObjectLiteralExpressionBase { + parent: JsxOpeningLikeElement; +} + +/// The opening element of a ... JsxElement +export interface JsxOpeningElement extends Expression { + kind: SyntaxKind.JsxOpeningElement; + parent: JsxElement; + tagName: JsxTagNameExpression; + typeArguments?: NodeArray; + attributes: JsxAttributes; +} + +/// A JSX expression of the form +export interface JsxSelfClosingElement extends PrimaryExpression { + kind: SyntaxKind.JsxSelfClosingElement; + tagName: JsxTagNameExpression; + typeArguments?: NodeArray; + attributes: JsxAttributes; +} + +/// A JSX expression of the form <>... +export interface JsxFragment extends PrimaryExpression { + kind: SyntaxKind.JsxFragment; + openingFragment: JsxOpeningFragment; + children: NodeArray; + closingFragment: JsxClosingFragment; +} + +/// The opening element of a <>... JsxFragment +export interface JsxOpeningFragment extends Expression { + kind: SyntaxKind.JsxOpeningFragment; + parent: JsxFragment; +} + +/// The closing element of a <>... JsxFragment +export interface JsxClosingFragment extends Expression { + kind: SyntaxKind.JsxClosingFragment; + parent: JsxFragment; +} + +export interface JsxAttribute extends ObjectLiteralElement { + kind: SyntaxKind.JsxAttribute; + parent: JsxAttributes; + name: Identifier; + /// JSX attribute initializers are optional; is sugar for + initializer?: StringLiteral | JsxExpression; +} + +export interface JsxSpreadAttribute extends ObjectLiteralElement { + kind: SyntaxKind.JsxSpreadAttribute; + parent: JsxAttributes; + expression: Expression; +} + +export interface JsxClosingElement extends Node { + kind: SyntaxKind.JsxClosingElement; + parent: JsxElement; + tagName: JsxTagNameExpression; +} + +export interface JsxExpression extends Expression { + kind: SyntaxKind.JsxExpression; + parent: JsxElement | JsxAttributeLike; + dotDotDotToken?: Token; + expression?: Expression; +} + +export interface JsxText extends Node { + kind: SyntaxKind.JsxText; + containsOnlyWhiteSpaces: boolean; + parent: JsxElement; +} + +export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; + +export interface Statement extends Node { + _statementBrand: any; +} + +// Represents a statement that is elided as part of a transformation to emit comments on a +// not-emitted node. +export interface NotEmittedStatement extends Statement { + kind: SyntaxKind.NotEmittedStatement; +} + +/** + * Marks the end of transformed declaration to properly emit exports. + */ +/* @internal */ +export interface EndOfDeclarationMarker extends Statement { + kind: SyntaxKind.EndOfDeclarationMarker; +} + +/** + * A list of comma-separated expressions. This node is only created by transformations. + */ +export interface CommaListExpression extends Expression { + kind: SyntaxKind.CommaListExpression; + elements: NodeArray; +} + +/** + * Marks the beginning of a merged transformed declaration. + */ +/* @internal */ +export interface MergeDeclarationMarker extends Statement { + kind: SyntaxKind.MergeDeclarationMarker; +} + +export interface EmptyStatement extends Statement { + kind: SyntaxKind.EmptyStatement; +} + +export interface DebuggerStatement extends Statement { + kind: SyntaxKind.DebuggerStatement; +} + +export interface MissingDeclaration extends DeclarationStatement { + kind: SyntaxKind.MissingDeclaration; + name?: Identifier; +} + +export type BlockLike = SourceFile | Block | ModuleBlock | CaseOrDefaultClause; + +export interface Block extends Statement { + kind: SyntaxKind.Block; + statements: NodeArray; + /*@internal*/ multiLine?: boolean; +} + +export interface VariableStatement extends Statement, JSDocContainer { + kind: SyntaxKind.VariableStatement; + declarationList: VariableDeclarationList; +} + +export interface ExpressionStatement extends Statement, JSDocContainer { + kind: SyntaxKind.ExpressionStatement; + expression: Expression; +} + +/* @internal */ +export interface PrologueDirective extends ExpressionStatement { + expression: StringLiteral; +} + +export interface IfStatement extends Statement { + kind: SyntaxKind.IfStatement; + expression: Expression; + thenStatement: Statement; + elseStatement?: Statement; +} + +export interface IterationStatement extends Statement { + statement: Statement; +} + +export interface DoStatement extends IterationStatement { + kind: SyntaxKind.DoStatement; + expression: Expression; +} + +export interface WhileStatement extends IterationStatement { + kind: SyntaxKind.WhileStatement; + expression: Expression; +} + +export type ForInitializer = VariableDeclarationList | Expression; + +export interface ForStatement extends IterationStatement { + kind: SyntaxKind.ForStatement; + initializer?: ForInitializer; + condition?: Expression; + incrementor?: Expression; +} + +export type ForInOrOfStatement = ForInStatement | ForOfStatement; + +export interface ForInStatement extends IterationStatement { + kind: SyntaxKind.ForInStatement; + initializer: ForInitializer; + expression: Expression; +} + +export interface ForOfStatement extends IterationStatement { + kind: SyntaxKind.ForOfStatement; + awaitModifier?: AwaitKeywordToken; + initializer: ForInitializer; + expression: Expression; +} + +export interface BreakStatement extends Statement { + kind: SyntaxKind.BreakStatement; + label?: Identifier; +} + +export interface ContinueStatement extends Statement { + kind: SyntaxKind.ContinueStatement; + label?: Identifier; +} + +export type BreakOrContinueStatement = BreakStatement | ContinueStatement; + +export interface ReturnStatement extends Statement { + kind: SyntaxKind.ReturnStatement; + expression?: Expression; +} + +export interface WithStatement extends Statement { + kind: SyntaxKind.WithStatement; + expression: Expression; + statement: Statement; +} + +export interface SwitchStatement extends Statement { + kind: SyntaxKind.SwitchStatement; + expression: Expression; + caseBlock: CaseBlock; + possiblyExhaustive?: boolean; +} + +export interface CaseBlock extends Node { + kind: SyntaxKind.CaseBlock; + parent: SwitchStatement; + clauses: NodeArray; +} + +export interface CaseClause extends Node { + kind: SyntaxKind.CaseClause; + parent: CaseBlock; + expression: Expression; + statements: NodeArray; +} + +export interface DefaultClause extends Node { + kind: SyntaxKind.DefaultClause; + parent: CaseBlock; + statements: NodeArray; +} + +export type CaseOrDefaultClause = CaseClause | DefaultClause; + +export interface LabeledStatement extends Statement, JSDocContainer { + kind: SyntaxKind.LabeledStatement; + label: Identifier; + statement: Statement; +} + +export interface ThrowStatement extends Statement { + kind: SyntaxKind.ThrowStatement; + expression?: Expression; +} + +export interface TryStatement extends Statement { + kind: SyntaxKind.TryStatement; + tryBlock: Block; + catchClause?: CatchClause; + finallyBlock?: Block; +} + +export interface CatchClause extends Node { + kind: SyntaxKind.CatchClause; + parent: TryStatement; + variableDeclaration?: VariableDeclaration; + block: Block; +} + +export type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode; + +export type DeclarationWithTypeParameters = DeclarationWithTypeParameterChildren | JSDocTypedefTag | JSDocCallbackTag | JSDocSignature; +export type DeclarationWithTypeParameterChildren = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag; + +export interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer { + kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression; + name?: Identifier; + typeParameters?: NodeArray; + heritageClauses?: NodeArray; + members: NodeArray; +} + +export interface ClassDeclaration extends ClassLikeDeclarationBase, DeclarationStatement { + kind: SyntaxKind.ClassDeclaration; + /** May be undefined in `export default class { ... }`. */ + name?: Identifier; +} + +export interface ClassExpression extends ClassLikeDeclarationBase, PrimaryExpression { + kind: SyntaxKind.ClassExpression; +} + +export type ClassLikeDeclaration = ClassDeclaration | ClassExpression; + +export interface ClassElement extends NamedDeclaration { + _classElementBrand: any; + name?: PropertyName; +} + +export interface TypeElement extends NamedDeclaration { + _typeElementBrand: any; + name?: PropertyName; + questionToken?: QuestionToken; +} + +export interface InterfaceDeclaration extends DeclarationStatement, JSDocContainer { + kind: SyntaxKind.InterfaceDeclaration; + name: Identifier; + typeParameters?: NodeArray; + heritageClauses?: NodeArray; + members: NodeArray; +} + +export interface HeritageClause extends Node { + kind: SyntaxKind.HeritageClause; + parent: InterfaceDeclaration | ClassLikeDeclaration; + token: SyntaxKind.ExtendsKeyword | SyntaxKind.ImplementsKeyword; + types: NodeArray; +} + +export interface TypeAliasDeclaration extends DeclarationStatement, JSDocContainer { + kind: SyntaxKind.TypeAliasDeclaration; + name: Identifier; + typeParameters?: NodeArray; + type: TypeNode; +} + +export interface EnumMember extends NamedDeclaration, JSDocContainer { + kind: SyntaxKind.EnumMember; + parent: EnumDeclaration; + // This does include ComputedPropertyName, but the parser will give an error + // if it parses a ComputedPropertyName in an EnumMember + name: PropertyName; + initializer?: Expression; +} + +export interface EnumDeclaration extends DeclarationStatement, JSDocContainer { + kind: SyntaxKind.EnumDeclaration; + name: Identifier; + members: NodeArray; +} + +export type ModuleName = Identifier | StringLiteral; + +export type ModuleBody = NamespaceBody | JSDocNamespaceBody; + +/* @internal */ +export interface AmbientModuleDeclaration extends ModuleDeclaration { body?: ModuleBlock; } + +export interface ModuleDeclaration extends DeclarationStatement, JSDocContainer { + kind: SyntaxKind.ModuleDeclaration; + parent: ModuleBody | SourceFile; + name: ModuleName; + body?: ModuleBody | JSDocNamespaceDeclaration; +} + +export type NamespaceBody = ModuleBlock | NamespaceDeclaration; + +export interface NamespaceDeclaration extends ModuleDeclaration { + name: Identifier; + body: NamespaceBody; +} + +export type JSDocNamespaceBody = Identifier | JSDocNamespaceDeclaration; + +export interface JSDocNamespaceDeclaration extends ModuleDeclaration { + name: Identifier; + body?: JSDocNamespaceBody; +} + +export interface ModuleBlock extends Node, Statement { + kind: SyntaxKind.ModuleBlock; + parent: ModuleDeclaration; + statements: NodeArray; +} + +export type ModuleReference = EntityName | ExternalModuleReference; + +/** + * One of: + * - import x = require("mod"); + * - import x = M.x; + */ +export interface ImportEqualsDeclaration extends DeclarationStatement, JSDocContainer { + kind: SyntaxKind.ImportEqualsDeclaration; + parent: SourceFile | ModuleBlock; + name: Identifier; + + // 'EntityName' for an internal module reference, 'ExternalModuleReference' for an external + // module reference. + moduleReference: ModuleReference; +} + +export interface ExternalModuleReference extends Node { + kind: SyntaxKind.ExternalModuleReference; + parent: ImportEqualsDeclaration; + expression: Expression; +} + +// In case of: +// import "mod" => importClause = undefined, moduleSpecifier = "mod" +// In rest of the cases, module specifier is string literal corresponding to module +// ImportClause information is shown at its declaration below. +export interface ImportDeclaration extends Statement { + kind: SyntaxKind.ImportDeclaration; + parent: SourceFile | ModuleBlock; + importClause?: ImportClause; + /** If this is not a StringLiteral it will be a grammar error. */ + moduleSpecifier: Expression; +} + +export type NamedImportBindings = NamespaceImport | NamedImports; + +// In case of: +// import d from "mod" => name = d, namedBinding = undefined +// import * as ns from "mod" => name = undefined, namedBinding: NamespaceImport = { name: ns } +// import d, * as ns from "mod" => name = d, namedBinding: NamespaceImport = { name: ns } +// import { a, b as x } from "mod" => name = undefined, namedBinding: NamedImports = { elements: [{ name: a }, { name: x, propertyName: b}]} +// import d, { a, b as x } from "mod" => name = d, namedBinding: NamedImports = { elements: [{ name: a }, { name: x, propertyName: b}]} +export interface ImportClause extends NamedDeclaration { + kind: SyntaxKind.ImportClause; + parent: ImportDeclaration; + name?: Identifier; // Default binding + namedBindings?: NamedImportBindings; +} + +export interface NamespaceImport extends NamedDeclaration { + kind: SyntaxKind.NamespaceImport; + parent: ImportClause; + name: Identifier; +} + +export interface NamespaceExportDeclaration extends DeclarationStatement { + kind: SyntaxKind.NamespaceExportDeclaration; + name: Identifier; +} + +export interface ExportDeclaration extends DeclarationStatement, JSDocContainer { + kind: SyntaxKind.ExportDeclaration; + parent: SourceFile | ModuleBlock; + /** Will not be assigned in the case of `export * from "foo";` */ + exportClause?: NamedExports; + /** If this is not a StringLiteral it will be a grammar error. */ + moduleSpecifier?: Expression; +} + +export interface NamedImports extends Node { + kind: SyntaxKind.NamedImports; + parent: ImportClause; + elements: NodeArray; +} + +export interface NamedExports extends Node { + kind: SyntaxKind.NamedExports; + parent: ExportDeclaration; + elements: NodeArray; +} + +export type NamedImportsOrExports = NamedImports | NamedExports; + +export interface ImportSpecifier extends NamedDeclaration { + kind: SyntaxKind.ImportSpecifier; + parent: NamedImports; + propertyName?: Identifier; // Name preceding "as" keyword (or undefined when "as" is absent) + name: Identifier; // Declared name +} + +export interface ExportSpecifier extends NamedDeclaration { + kind: SyntaxKind.ExportSpecifier; + parent: NamedExports; + propertyName?: Identifier; // Name preceding "as" keyword (or undefined when "as" is absent) + name: Identifier; // Declared name +} + +export type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier; + +/** + * This is either an `export =` or an `export default` declaration. + * Unless `isExportEquals` is set, this node was parsed as an `export default`. + */ +export interface ExportAssignment extends DeclarationStatement { + kind: SyntaxKind.ExportAssignment; + parent: SourceFile; + isExportEquals?: boolean; + expression: Expression; +} + +export interface FileReference extends TextRange { + fileName: string; +} + +export interface CheckJsDirective extends TextRange { + enabled: boolean; +} + +export type CommentKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia; + +export interface CommentRange extends TextRange { + hasTrailingNewLine?: boolean; + kind: CommentKind; +} + +export interface SynthesizedComment extends CommentRange { + text: string; + pos: -1; + end: -1; +} + +// represents a top level: { type } expression in a JSDoc comment. +export interface JSDocTypeExpression extends TypeNode { + kind: SyntaxKind.JSDocTypeExpression; + type: TypeNode; +} + +export interface JSDocType extends TypeNode { + _jsDocTypeBrand: any; +} + +export interface JSDocAllType extends JSDocType { + kind: SyntaxKind.JSDocAllType; +} + +export interface JSDocUnknownType extends JSDocType { + kind: SyntaxKind.JSDocUnknownType; +} + +export interface JSDocNonNullableType extends JSDocType { + kind: SyntaxKind.JSDocNonNullableType; + type: TypeNode; +} + +export interface JSDocNullableType extends JSDocType { + kind: SyntaxKind.JSDocNullableType; + type: TypeNode; +} + +export interface JSDocOptionalType extends JSDocType { + kind: SyntaxKind.JSDocOptionalType; + type: TypeNode; +} + +export interface JSDocFunctionType extends JSDocType, SignatureDeclarationBase { + kind: SyntaxKind.JSDocFunctionType; +} + +export interface JSDocVariadicType extends JSDocType { + kind: SyntaxKind.JSDocVariadicType; + type: TypeNode; +} + +export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; + +export interface JSDoc extends Node { + kind: SyntaxKind.JSDocComment; + parent: HasJSDoc; + tags?: NodeArray; + comment?: string; +} + +export interface JSDocTag extends Node { + parent: JSDoc | JSDocTypeLiteral; + atToken: AtToken; + tagName: Identifier; + comment?: string; +} + +export interface JSDocUnknownTag extends JSDocTag { + kind: SyntaxKind.JSDocTag; +} + +/** + * Note that `@extends` is a synonym of `@augments`. + * Both tags are represented by this interface. + */ +export interface JSDocAugmentsTag extends JSDocTag { + kind: SyntaxKind.JSDocAugmentsTag; + class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; +} + +export interface JSDocClassTag extends JSDocTag { + kind: SyntaxKind.JSDocClassTag; +} + +export interface JSDocEnumTag extends JSDocTag { + kind: SyntaxKind.JSDocEnumTag; + typeExpression?: JSDocTypeExpression; +} + +export interface JSDocThisTag extends JSDocTag { + kind: SyntaxKind.JSDocThisTag; + typeExpression?: JSDocTypeExpression; +} + +export interface JSDocTemplateTag extends JSDocTag { + kind: SyntaxKind.JSDocTemplateTag; + constraint: TypeNode | undefined; + typeParameters: NodeArray; +} + +export interface JSDocReturnTag extends JSDocTag { + kind: SyntaxKind.JSDocReturnTag; + typeExpression?: JSDocTypeExpression; +} + +export interface JSDocTypeTag extends JSDocTag { + kind: SyntaxKind.JSDocTypeTag; + typeExpression?: JSDocTypeExpression; +} + +export interface JSDocTypedefTag extends JSDocTag, NamedDeclaration { + parent: JSDoc; + kind: SyntaxKind.JSDocTypedefTag; + fullName?: JSDocNamespaceDeclaration | Identifier; + name?: Identifier; + typeExpression?: JSDocTypeExpression | JSDocTypeLiteral; +} + +export interface JSDocCallbackTag extends JSDocTag, NamedDeclaration { + parent: JSDoc; + kind: SyntaxKind.JSDocCallbackTag; + fullName?: JSDocNamespaceDeclaration | Identifier; + name?: Identifier; + typeExpression: JSDocSignature; +} + +export interface JSDocSignature extends JSDocType, Declaration { + kind: SyntaxKind.JSDocSignature; + typeParameters?: ReadonlyArray; + parameters: ReadonlyArray; + type: JSDocReturnTag | undefined; +} + +export interface JSDocPropertyLikeTag extends JSDocTag, Declaration { + parent: JSDoc; + name: EntityName; + typeExpression?: JSDocTypeExpression; + /** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */ + isNameFirst: boolean; + isBracketed: boolean; +} + +export interface JSDocPropertyTag extends JSDocPropertyLikeTag { + kind: SyntaxKind.JSDocPropertyTag; +} + +export interface JSDocParameterTag extends JSDocPropertyLikeTag { + kind: SyntaxKind.JSDocParameterTag; +} + +export interface JSDocTypeLiteral extends JSDocType { + kind: SyntaxKind.JSDocTypeLiteral; + jsDocPropertyTags?: ReadonlyArray; + /** If true, then this type literal represents an *array* of its type. */ + isArrayType?: boolean; +} + +export const enum FlowFlags { + Unreachable = 1 << 0, // Unreachable code + Start = 1 << 1, // Start of flow graph + BranchLabel = 1 << 2, // Non-looping junction + LoopLabel = 1 << 3, // Looping junction + Assignment = 1 << 4, // Assignment + TrueCondition = 1 << 5, // Condition known to be true + FalseCondition = 1 << 6, // Condition known to be false + SwitchClause = 1 << 7, // Switch statement clause + ArrayMutation = 1 << 8, // Potential array mutation + Referenced = 1 << 9, // Referenced as antecedent once + Shared = 1 << 10, // Referenced as antecedent more than once + PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow + AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph + Label = BranchLabel | LoopLabel, + Condition = TrueCondition | FalseCondition +} + +export interface FlowLock { + locked?: boolean; +} + +export interface AfterFinallyFlow extends FlowNodeBase, FlowLock { + antecedent: FlowNode; +} + +export interface PreFinallyFlow extends FlowNodeBase { + antecedent: FlowNode; + lock: FlowLock; +} + +export type FlowNode = + | AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation; +export interface FlowNodeBase { + flags: FlowFlags; + id?: number; // Node id used by flow type cache in checker +} + +// FlowStart represents the start of a control flow. For a function expression or arrow +// function, the container property references the function (which in turn has a flowNode +// property for the containing control flow). +export interface FlowStart extends FlowNodeBase { + container?: FunctionExpression | ArrowFunction | MethodDeclaration; +} + +// FlowLabel represents a junction with multiple possible preceding control flows. +export interface FlowLabel extends FlowNodeBase { + antecedents: FlowNode[] | undefined; +} + +// FlowAssignment represents a node that assigns a value to a narrowable reference, +// i.e. an identifier or a dotted name that starts with an identifier or 'this'. +export interface FlowAssignment extends FlowNodeBase { + node: Expression | VariableDeclaration | BindingElement; + antecedent: FlowNode; +} + +// FlowCondition represents a condition that is known to be true or false at the +// node's location in the control flow. +export interface FlowCondition extends FlowNodeBase { + expression: Expression; + antecedent: FlowNode; +} + +export interface FlowSwitchClause extends FlowNodeBase { + switchStatement: SwitchStatement; + clauseStart: number; // Start index of case/default clause range + clauseEnd: number; // End index of case/default clause range + antecedent: FlowNode; +} + +// FlowArrayMutation represents a node potentially mutates an array, i.e. an +// operation of the form 'x.push(value)', 'x.unshift(value)' or 'x[n] = value'. +export interface FlowArrayMutation extends FlowNodeBase { + node: CallExpression | BinaryExpression; + antecedent: FlowNode; +} + +export type FlowType = Type | IncompleteType; + +// Incomplete types occur during control flow analysis of loops. An IncompleteType +// is distinguished from a regular type by a flags value of zero. Incomplete type +// objects are internal to the getFlowTypeOfRefecence function and never escape it. +export interface IncompleteType { + flags: TypeFlags; // No flags set + type: Type; // The type marked incomplete +} + +export interface AmdDependency { + path: string; + name?: string; +} + +/* @internal */ +/** + * Subset of properties from SourceFile that are used in multiple utility functions + */ +export interface SourceFileLike { + readonly text: string; + lineMap?: ReadonlyArray; +} + + +/* @internal */ +export interface RedirectInfo { + /** Source file this redirects to. */ + readonly redirectTarget: SourceFile; + /** + * Source file for the duplicate package. This will not be used by the Program, + * but we need to keep this around so we can watch for changes in underlying. + */ + readonly unredirected: SourceFile; +} + +// Source files are declarations when they are external modules. +export interface SourceFile extends Declaration { + kind: SyntaxKind.SourceFile; + statements: NodeArray; + endOfFileToken: Token; + + fileName: string; + /* @internal */ path: Path; + text: string; + /** Resolved path can be different from path property, + * when file is included through project reference is mapped to its output instead of source + * in that case resolvedPath = path to output file + * path = input file's path + */ + /* @internal */ resolvedPath: Path; + /** Original file name that can be different from fileName, + * when file is included through project reference is mapped to its output instead of source + * in that case originalFileName = name of input file + * fileName = output file's name + */ + /* @internal */ originalFileName: string; + + /** + * If two source files are for the same version of the same package, one will redirect to the other. + * (See `createRedirectSourceFile` in program.ts.) + * The redirect will have this set. The redirected-to source file will be in `redirectTargetsMap`. + */ + /* @internal */ redirectInfo?: RedirectInfo; + + amdDependencies: ReadonlyArray; + moduleName?: string; + referencedFiles: ReadonlyArray; + typeReferenceDirectives: ReadonlyArray; + libReferenceDirectives: ReadonlyArray; + languageVariant: LanguageVariant; + isDeclarationFile: boolean; + + // this map is used by transpiler to supply alternative names for dependencies (i.e. in case of bundling) + /* @internal */ + renamedDependencies?: ReadonlyMap; + + /** + * lib.d.ts should have a reference comment like + * + * /// + * + * If any other file has this comment, it signals not to include lib.d.ts + * because this containing file is intended to act as a default library. + */ + hasNoDefaultLib: boolean; + + languageVersion: ScriptTarget; + /* @internal */ scriptKind: ScriptKind; + + /** + * The first "most obvious" node that makes a file an external module. + * This is intended to be the first top-level import/export, + * but could be arbitrarily nested (e.g. `import.meta`). + */ + /* @internal */ externalModuleIndicator?: Node; + // The first node that causes this file to be a CommonJS module + /* @internal */ commonJsModuleIndicator?: Node; + // JS identifier-declarations that are intended to merge with globals + /* @internal */ jsGlobalAugmentations?: SymbolTable; + + /* @internal */ identifiers: Map; // Map from a string to an interned string + /* @internal */ nodeCount: number; + /* @internal */ identifierCount: number; + /* @internal */ symbolCount: number; + + // File-level diagnostics reported by the parser (includes diagnostics about /// references + // as well as code diagnostics). + /* @internal */ parseDiagnostics: DiagnosticWithLocation[]; + + // File-level diagnostics reported by the binder. + /* @internal */ bindDiagnostics: DiagnosticWithLocation[]; + /* @internal */ bindSuggestionDiagnostics?: DiagnosticWithLocation[]; + + // File-level JSDoc diagnostics reported by the JSDoc parser + /* @internal */ jsDocDiagnostics?: DiagnosticWithLocation[]; + + // Stores additional file-level diagnostics reported by the program + /* @internal */ additionalSyntacticDiagnostics?: ReadonlyArray; + + // Stores a line map for the file. + // This field should never be used directly to obtain line map, use getLineMap function instead. + /* @internal */ lineMap: ReadonlyArray; + /* @internal */ classifiableNames?: ReadonlyUnderscoreEscapedMap; + // Stores a mapping 'external module reference text' -> 'resolved file name' | undefined + // It is used to resolve module names in the checker. + // Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead + /* @internal */ resolvedModules?: Map; + /* @internal */ resolvedTypeReferenceDirectiveNames: Map; + /* @internal */ imports: ReadonlyArray; + /** + * When a file's references are redirected due to project reference directives, + * the original names of the references are stored in this array + */ + /* @internal*/ + redirectedReferences?: ReadonlyArray; + // Identifier only if `declare global` + /* @internal */ moduleAugmentations: ReadonlyArray; + /* @internal */ patternAmbientModules?: PatternAmbientModule[]; + /* @internal */ ambientModuleNames: ReadonlyArray; + /* @internal */ checkJsDirective?: CheckJsDirective; + /* @internal */ version: string; + /* @internal */ pragmas: ReadonlyPragmaMap; + /* @internal */ localJsxNamespace?: __String; + /* @internal */ localJsxFactory?: EntityName; + + /*@internal*/ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; +} + +/*@internal*/ +export type ExportedModulesFromDeclarationEmit = ReadonlyArray; + +export interface Bundle extends Node { + kind: SyntaxKind.Bundle; + prepends: ReadonlyArray; + sourceFiles: ReadonlyArray; + /* @internal */ syntheticFileReferences?: ReadonlyArray; + /* @internal */ syntheticTypeReferences?: ReadonlyArray; + /* @internal */ syntheticLibReferences?: ReadonlyArray; + /* @internal */ hasNoDefaultLib?: boolean; +} + +export interface InputFiles extends Node { + kind: SyntaxKind.InputFiles; + javascriptText: string; + javascriptMapPath?: string; + javascriptMapText?: string; + declarationText: string; + declarationMapPath?: string; + declarationMapText?: string; +} + +export interface UnparsedSource extends Node { + kind: SyntaxKind.UnparsedSource; + text: string; + sourceMapPath?: string; + sourceMapText?: string; +} + +export interface JsonSourceFile extends SourceFile { + statements: NodeArray; +} + +export interface TsConfigSourceFile extends JsonSourceFile { + extendedSourceFiles?: string[]; +} + +export interface JsonMinusNumericLiteral extends PrefixUnaryExpression { + kind: SyntaxKind.PrefixUnaryExpression; + operator: SyntaxKind.MinusToken; + operand: NumericLiteral; +} + +export interface JsonObjectExpressionStatement extends ExpressionStatement { + expression: ObjectLiteralExpression | ArrayLiteralExpression | JsonMinusNumericLiteral | NumericLiteral | StringLiteral | BooleanLiteral | NullLiteral; +} + +export interface ScriptReferenceHost { + getCompilerOptions(): CompilerOptions; + getSourceFile(fileName: string): SourceFile | undefined; + getSourceFileByPath(path: Path): SourceFile | undefined; + getCurrentDirectory(): string; +} + +export interface ParseConfigHost { + useCaseSensitiveFileNames: boolean; + + readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; + + /** + * Gets a value indicating whether the specified path exists and is a file. + * @param path The path to test. + */ + fileExists(path: string): boolean; + + readFile(path: string): string | undefined; +} + +/** + * Branded string for keeping track of when we've turned an ambiguous path + * specified like "./blah" to an absolute path to an actual + * tsconfig file, e.g. "/root/blah/tsconfig.json" + */ +export type ResolvedConfigFileName = string & { _isResolvedConfigFileName: never }; + +export type WriteFileCallback = ( + fileName: string, + data: string, + writeByteOrderMark: boolean, + onError: ((message: string) => void) | undefined, + sourceFiles?: ReadonlyArray, +) => void; + +export class OperationCanceledException { } + +export interface CancellationToken { + isCancellationRequested(): boolean; + + /** @throws OperationCanceledException if isCancellationRequested is true */ + throwIfCancellationRequested(): void; +} + +// TODO: This should implement TypeCheckerHost but that's an internal type. +export interface Program extends ScriptReferenceHost { + + /** + * Get a list of root file names that were passed to a 'createProgram' + */ + getRootFileNames(): ReadonlyArray; + + /** + * Get a list of files in the program + */ + getSourceFiles(): ReadonlyArray; + + /** + * Get a list of file names that were passed to 'createProgram' or referenced in a + * program source file but could not be located. + */ + /* @internal */ + getMissingFilePaths(): ReadonlyArray; + + /** + * Emits the JavaScript and declaration files. If targetSourceFile is not specified, then + * the JavaScript and declaration files will be produced for all the files in this program. + * If targetSourceFile is specified, then only the JavaScript and declaration for that + * specific file will be generated. + * + * If writeFile is not specified then the writeFile callback from the compiler host will be + * used for writing the JavaScript and declaration files. Otherwise, the writeFile parameter + * will be invoked when writing the JavaScript and declaration files. + */ + emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult; + + getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray; + getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray; + getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + /** The first time this is called, it will return global diagnostics (no location). */ + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + getConfigFileParsingDiagnostics(): ReadonlyArray; + /* @internal */ getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + + /** + * Gets a type checker that can be used to semantically analyze source files in the program. + */ + getTypeChecker(): TypeChecker; + + /* @internal */ getCommonSourceDirectory(): string; + + // For testing purposes only. Should not be used by any other consumers (including the + // language service). + /* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker; + /* @internal */ dropDiagnosticsProducingTypeChecker(): void; + + /* @internal */ getClassifiableNames(): UnderscoreEscapedMap; + + /* @internal */ getNodeCount(): number; + /* @internal */ getIdentifierCount(): number; + /* @internal */ getSymbolCount(): number; + /* @internal */ getTypeCount(): number; + + /* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection; + /* @internal */ getResolvedTypeReferenceDirectives(): Map; + isSourceFileFromExternalLibrary(file: SourceFile): boolean; + isSourceFileDefaultLibrary(file: SourceFile): boolean; + + // For testing purposes only. + /* @internal */ structureIsReused?: StructureIsReused; + + /* @internal */ getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined; + /* @internal */ getLibFileFromReference(ref: FileReference): SourceFile | undefined; + + /** Given a source file, get the name of the package it was imported from. */ + /* @internal */ sourceFileToPackageName: Map; + /** Set of all source files that some other source file redirects to. */ + /* @internal */ redirectTargetsMap: MultiMap; + /** Is the file emitted file */ + /* @internal */ isEmittedFile(file: string): boolean; + + /* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined; + + getProjectReferences(): ReadonlyArray | undefined; + getResolvedProjectReferences(): ReadonlyArray | undefined; + /*@internal*/ getProjectReferenceRedirect(fileName: string): string | undefined; + /*@internal*/ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined; + /*@internal*/ forEachResolvedProjectReference(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined; + /*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined; +} + +/* @internal */ +export type RedirectTargetsMap = ReadonlyMap>; + +export interface ResolvedProjectReference { + commandLine: ParsedCommandLine; + sourceFile: SourceFile; + references?: ReadonlyArray; +} + +/* @internal */ +export const enum StructureIsReused { + Not = 0, + SafeModules = 1 << 0, + Completely = 1 << 1, +} + +export interface CustomTransformers { + /** Custom transformers to evaluate before built-in .js transformations. */ + before?: TransformerFactory[]; + /** Custom transformers to evaluate after built-in .js transformations. */ + after?: TransformerFactory[]; + /** Custom transformers to evaluate after built-in .d.ts transformations. */ + afterDeclarations?: TransformerFactory[]; +} + +export interface SourceMapSpan { + /** Line number in the .js file. */ + emittedLine: number; + /** Column number in the .js file. */ + emittedColumn: number; + /** Line number in the .ts file. */ + sourceLine: number; + /** Column number in the .ts file. */ + sourceColumn: number; + /** Optional name (index into names array) associated with this span. */ + nameIndex?: number; + /** .ts file (index into sources array) associated with this span */ + sourceIndex: number; +} + +export interface SourceMapData { + sourceMapFilePath: string; // Where the sourcemap file is written + jsSourceMappingURL: string; // source map URL written in the .js file + sourceMapFile: string; // Source map's file field - .js file name + sourceMapSourceRoot: string; // Source map's sourceRoot field - location where the sources will be present if not "" + sourceMapSources: string[]; // Source map's sources field - list of sources that can be indexed in this source map + sourceMapSourcesContent?: (string | null)[]; // Source map's sourcesContent field - list of the sources' text to be embedded in the source map + inputSourceFileNames: string[]; // Input source file (which one can use on program to get the file), 1:1 mapping with the sourceMapSources list + sourceMapNames?: string[]; // Source map's names field - list of names that can be indexed in this source map + sourceMapMappings: string; // Source map's mapping field - encoded source map spans +} + +/** Return code used by getEmitOutput function to indicate status of the function */ +export enum ExitStatus { + // Compiler ran successfully. Either this was a simple do-nothing compilation (for example, + // when -version or -help was provided, or this was a normal compilation, no diagnostics + // were produced, and all outputs were generated successfully. + Success = 0, + + // Diagnostics were produced and because of them no code was generated. + DiagnosticsPresent_OutputsSkipped = 1, + + // Diagnostics were produced and outputs were generated in spite of them. + DiagnosticsPresent_OutputsGenerated = 2, +} + +export interface EmitResult { + emitSkipped: boolean; + /** Contains declaration emit diagnostics */ + diagnostics: ReadonlyArray; + emittedFiles?: string[]; // Array of files the compiler wrote to disk + /* @internal */ sourceMaps?: SourceMapData[]; // Array of sourceMapData if compiler emitted sourcemaps + /* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; +} + +/* @internal */ +export interface TypeCheckerHost extends ModuleSpecifierResolutionHost { + getCompilerOptions(): CompilerOptions; + + getSourceFiles(): ReadonlyArray; + getSourceFile(fileName: string): SourceFile | undefined; + getResolvedTypeReferenceDirectives(): ReadonlyMap; + + readonly redirectTargetsMap: RedirectTargetsMap; +} + +export interface TypeChecker { + getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type; + getDeclaredTypeOfSymbol(symbol: Symbol): Type; + getPropertiesOfType(type: Type): Symbol[]; + getPropertyOfType(type: Type, propertyName: string): Symbol | undefined; + /* @internal */ getTypeOfPropertyOfType(type: Type, propertyName: string): Type | undefined; + getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined; + getSignaturesOfType(type: Type, kind: SignatureKind): ReadonlyArray; + getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined; + getBaseTypes(type: InterfaceType): BaseType[]; + getBaseTypeOfLiteralType(type: Type): Type; + getWidenedType(type: Type): Type; + /* @internal */ + getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type | undefined; + getReturnTypeOfSignature(signature: Signature): Type; + /** + * Gets the type of a parameter at a given position in a signature. + * Returns `any` if the index is not valid. + */ + /* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type; + getNullableType(type: Type, flags: TypeFlags): Type; + getNonNullableType(type: Type): Type; + + // TODO: GH#18217 `xToDeclaration` calls are frequently asserted as defined. + /** Note that the resulting nodes cannot be checked. */ + typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode | undefined; + /* @internal */ typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined; // tslint:disable-line unified-signatures + /** Note that the resulting nodes cannot be checked. */ + signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration & {typeArguments?: NodeArray} | undefined; + /** Note that the resulting nodes cannot be checked. */ + indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration | undefined; + /** Note that the resulting nodes cannot be checked. */ + symbolToEntityName(symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): EntityName | undefined; + /** Note that the resulting nodes cannot be checked. */ + symbolToExpression(symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Expression | undefined; + /** Note that the resulting nodes cannot be checked. */ + symbolToTypeParameterDeclarations(symbol: Symbol, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): NodeArray | undefined; + /** Note that the resulting nodes cannot be checked. */ + symbolToParameterDeclaration(symbol: Symbol, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): ParameterDeclaration | undefined; + /** Note that the resulting nodes cannot be checked. */ + typeParameterToDeclaration(parameter: TypeParameter, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeParameterDeclaration | undefined; + + getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; + getSymbolAtLocation(node: Node): Symbol | undefined; + getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[]; + /** + * The function returns the value (local variable) symbol of an identifier in the short-hand property assignment. + * This is necessary as an identifier in short-hand property assignment can contains two meaning: property name and property value. + */ + getShorthandAssignmentValueSymbol(location: Node): Symbol | undefined; + getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol | undefined; + /** + * If a symbol is a local symbol with an associated exported symbol, returns the exported symbol. + * Otherwise returns its input. + * For example, at `export type T = number;`: + * - `getSymbolAtLocation` at the location `T` will return the exported symbol for `T`. + * - But the result of `getSymbolsInScope` will contain the *local* symbol for `T`, not the exported symbol. + * - Calling `getExportSymbolOfSymbol` on that local symbol will return the exported symbol. + */ + getExportSymbolOfSymbol(symbol: Symbol): Symbol; + getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol | undefined; + getTypeAtLocation(node: Node): Type; + getTypeFromTypeNode(node: TypeNode): Type; + + signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string; + typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; + symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): string; + typePredicateToString(predicate: TypePredicate, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; + + /* @internal */ writeSignature(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind, writer?: EmitTextWriter): string; + /* @internal */ writeType(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags, writer?: EmitTextWriter): string; + /* @internal */ writeSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags, writer?: EmitTextWriter): string; + /* @internal */ writeTypePredicate(predicate: TypePredicate, enclosingDeclaration?: Node, flags?: TypeFormatFlags, writer?: EmitTextWriter): string; + + getFullyQualifiedName(symbol: Symbol): string; + getAugmentedPropertiesOfType(type: Type): Symbol[]; + getRootSymbols(symbol: Symbol): Symbol[]; + getContextualType(node: Expression): Type | undefined; + /* @internal */ getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined; + /* @internal */ getContextualTypeForArgumentAtIndex(call: CallLikeExpression, argIndex: number): Type | undefined; + /* @internal */ getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined; + /* @internal */ isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean; + + /** + * returns unknownSignature in the case of an error. + * returns undefined if the node is not valid. + * @param argumentCount Apparent number of arguments, passed in case of a possibly incomplete call. This should come from an ArgumentListInfo. See `signatureHelp.ts`. + */ + getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined; + /* @internal */ getResolvedSignatureForSignatureHelp(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined; + getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature | undefined; + isImplementationOfOverload(node: SignatureDeclaration): boolean | undefined; + isUndefinedSymbol(symbol: Symbol): boolean; + isArgumentsSymbol(symbol: Symbol): boolean; + isUnknownSymbol(symbol: Symbol): boolean; + /* @internal */ getMergedSymbol(symbol: Symbol): Symbol; + + getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; + isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: string): boolean; + /** Exclude accesses to private properties or methods with a `this` parameter that `type` doesn't satisfy. */ + /* @internal */ isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode, type: Type, property: Symbol): boolean; + /** Follow all aliases to get the original symbol. */ + getAliasedSymbol(symbol: Symbol): Symbol; + /** Follow a *single* alias to get the immediately aliased symbol. */ + /* @internal */ getImmediateAliasedSymbol(symbol: Symbol): Symbol | undefined; + getExportsOfModule(moduleSymbol: Symbol): Symbol[]; + /** Unlike `getExportsOfModule`, this includes properties of an `export =` value. */ + /* @internal */ getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[]; + getJsxIntrinsicTagNamesAt(location: Node): Symbol[]; + isOptionalParameter(node: ParameterDeclaration): boolean; + getAmbientModules(): Symbol[]; + + tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined; + /** + * Unlike `tryGetMemberInModuleExports`, this includes properties of an `export =` value. + * Does *not* return properties of primitive types. + */ + /* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined; + getApparentType(type: Type): Type; + /* @internal */ getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined; + /* @internal */ getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; + /* @internal */ getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined; + getBaseConstraintOfType(type: Type): Type | undefined; + getDefaultFromTypeParameter(type: Type): Type | undefined; + + /* @internal */ getAnyType(): Type; + /* @internal */ getStringType(): Type; + /* @internal */ getNumberType(): Type; + /* @internal */ getBooleanType(): Type; + /* @internal */ getFalseType(fresh?: boolean): Type; + /* @internal */ getTrueType(fresh?: boolean): Type; + /* @internal */ getVoidType(): Type; + /* @internal */ getUndefinedType(): Type; + /* @internal */ getNullType(): Type; + /* @internal */ getESSymbolType(): Type; + /* @internal */ getNeverType(): Type; + /* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type; + /* @internal */ createArrayType(elementType: Type): Type; + /* @internal */ createPromiseType(type: Type): Type; + + /* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): Type; + /* @internal */ createSignature( + declaration: SignatureDeclaration, + typeParameters: TypeParameter[] | undefined, + thisParameter: Symbol | undefined, + parameters: Symbol[], + resolvedReturnType: Type, + typePredicate: TypePredicate | undefined, + minArgumentCount: number, + hasRestParameter: boolean, + hasLiteralTypes: boolean, + ): Signature; + /* @internal */ createSymbol(flags: SymbolFlags, name: __String): TransientSymbol; + /* @internal */ createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo; + /* @internal */ isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult; + /* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol | undefined; + + /* @internal */ getSymbolWalker(accept?: (symbol: Symbol) => boolean): SymbolWalker; + + // Should not be called directly. Should only be accessed through the Program instance. + /* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; + /* @internal */ getGlobalDiagnostics(): Diagnostic[]; + /* @internal */ getEmitResolver(sourceFile?: SourceFile, cancellationToken?: CancellationToken): EmitResolver; + + /* @internal */ getNodeCount(): number; + /* @internal */ getIdentifierCount(): number; + /* @internal */ getSymbolCount(): number; + /* @internal */ getTypeCount(): number; + + /* @internal */ isArrayLikeType(type: Type): boolean; + /** + * True if `contextualType` should not be considered for completions because + * e.g. it specifies `kind: "a"` and obj has `kind: "b"`. + */ + /* @internal */ isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean; + /** + * For a union, will include a property if it's defined in *any* of the member types. + * So for `{ a } | { b }`, this will include both `a` and `b`. + * Does not include properties of primitive types. + */ + /* @internal */ getAllPossiblePropertiesOfTypes(type: ReadonlyArray): Symbol[]; + /* @internal */ resolveName(name: string, location: Node, meaning: SymbolFlags, excludeGlobals: boolean): Symbol | undefined; + /* @internal */ getJsxNamespace(location?: Node): string; + + /** + * Note that this will return undefined in the following case: + * // a.ts + * export namespace N { export class C { } } + * // b.ts + * <> + * Where `C` is the symbol we're looking for. + * This should be called in a loop climbing parents of the symbol, so we'll get `N`. + */ + /* @internal */ getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined; + /* @internal */ getTypePredicateOfSignature(signature: Signature): TypePredicate; + /** + * An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, + * and an external module with no 'export =' declaration resolves to the module itself. + */ + /* @internal */ resolveExternalModuleSymbol(symbol: Symbol): Symbol; + /** @param node A location where we might consider accessing `this`. Not necessarily a ThisExpression. */ + /* @internal */ tryGetThisTypeAt(node: Node): Type | undefined; + /* @internal */ getTypeArgumentConstraint(node: TypeNode): Type | undefined; + + /** + * Does *not* get *all* suggestion diagnostics, just the ones that were convenient to report in the checker. + * Others are added in computeSuggestionDiagnostics. + */ + /* @internal */ getSuggestionDiagnostics(file: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + + /** + * Depending on the operation performed, it may be appropriate to throw away the checker + * if the cancellation token is triggered. Typically, if it is used for error checking + * and the operation is cancelled, then it should be discarded, otherwise it is safe to keep. + */ + runWithCancellationToken(token: CancellationToken, cb: (checker: TypeChecker) => T): T; + + /* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): ReadonlyArray | undefined; +} + +/* @internal */ +export const enum UnionReduction { + None = 0, + Literal, + Subtype +} + +// NOTE: If modifying this enum, must modify `TypeFormatFlags` too! +export const enum NodeBuilderFlags { + None = 0, + // Options + NoTruncation = 1 << 0, // Don't truncate result + WriteArrayAsGenericType = 1 << 1, // Write Array instead T[] + GenerateNamesForShadowedTypeParams = 1 << 2, // When a type parameter T is shadowing another T, generate a name for it so it can still be referenced + UseStructuralFallback = 1 << 3, // When an alias cannot be named by its symbol, rather than report an error, fallback to a structural printout if possible + ForbidIndexedAccessSymbolReferences = 1 << 4, // Forbid references like `I["a"]["b"]` - print `typeof I.a.b` instead + WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature + UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type) + UseOnlyExternalAliasing = 1 << 7, // Only use external aliases for a symbol + SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type. + WriteTypeParametersInQualifiedName = 1 << 9, + MultilineObjectLiterals = 1 << 10, // Always write object literals across multiple lines + WriteClassExpressionAsTypeLiteral = 1 << 11, // Write class {} as { new(): {} } - used for mixin declaration emit + UseTypeOfFunction = 1 << 12, // Build using typeof instead of function type literal + OmitParameterModifiers = 1 << 13, // Omit modifiers on parameters + UseAliasDefinedOutsideCurrentScope = 1 << 14, // Allow non-visible aliases + + // Error handling + AllowThisInObjectLiteral = 1 << 15, + AllowQualifedNameInPlaceOfIdentifier = 1 << 16, + AllowAnonymousIdentifier = 1 << 17, + AllowEmptyUnionOrIntersection = 1 << 18, + AllowEmptyTuple = 1 << 19, + AllowUniqueESSymbolType = 1 << 20, + AllowEmptyIndexInfoType = 1 << 21, + + IgnoreErrors = AllowThisInObjectLiteral | AllowQualifedNameInPlaceOfIdentifier | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple | AllowEmptyIndexInfoType, + + // State + InObjectTypeLiteral = 1 << 22, + InTypeAlias = 1 << 23, // Writing type in type alias declaration + InInitialEntityName = 1 << 24, // Set when writing the LHS of an entity name or entity name expression + InReverseMappedType = 1 << 25, + + /* @internal */ DoNotIncludeSymbolChain = 1 << 26, // Skip looking up and printing an accessible symbol chain +} + +// Ensure the shared flags between this and `NodeBuilderFlags` stay in alignment +export const enum TypeFormatFlags { + None = 0, + NoTruncation = 1 << 0, // Don't truncate typeToString result + WriteArrayAsGenericType = 1 << 1, // Write Array instead T[] + // hole because there's a hole in node builder flags + UseStructuralFallback = 1 << 3, // When an alias cannot be named by its symbol, rather than report an error, fallback to a structural printout if possible + // hole because there's a hole in node builder flags + WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature + UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type) + // hole because `UseOnlyExternalAliasing` is here in node builder flags, but functions which take old flags use `SymbolFormatFlags` instead + SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type. + // hole because `WriteTypeParametersInQualifiedName` is here in node builder flags, but functions which take old flags use `SymbolFormatFlags` for this instead + MultilineObjectLiterals = 1 << 10, // Always print object literals across multiple lines (only used to map into node builder flags) + WriteClassExpressionAsTypeLiteral = 1 << 11, // Write a type literal instead of (Anonymous class) + UseTypeOfFunction = 1 << 12, // Write typeof instead of function type literal + OmitParameterModifiers = 1 << 13, // Omit modifiers on parameters + UseAliasDefinedOutsideCurrentScope = 1 << 14, // For a `type T = ... ` defined in a different file, write `T` instead of its value, + // even though `T` can't be accessed in the current scope. + + // Error Handling + AllowUniqueESSymbolType = 1 << 20, // This is bit 20 to align with the same bit in `NodeBuilderFlags` + + // TypeFormatFlags exclusive + AddUndefined = 1 << 17, // Add undefined to types of initialized, non-optional parameters + WriteArrowStyleSignature = 1 << 18, // Write arrow style signature + + // State + InArrayType = 1 << 19, // Writing an array element type + InElementType = 1 << 21, // Writing an array or union element type + InFirstTypeArgument = 1 << 22, // Writing first type argument of the instantiated type + InTypeAlias = 1 << 23, // Writing type in type alias declaration + + /** @deprecated */ WriteOwnNameForAnyLike = 0, // Does nothing + + NodeBuilderFlagsMask = + NoTruncation | WriteArrayAsGenericType | UseStructuralFallback | WriteTypeArgumentsOfSignature | + UseFullyQualifiedType | SuppressAnyReturnType | MultilineObjectLiterals | WriteClassExpressionAsTypeLiteral | + UseTypeOfFunction | OmitParameterModifiers | UseAliasDefinedOutsideCurrentScope | AllowUniqueESSymbolType | InTypeAlias, +} + +export const enum SymbolFormatFlags { + None = 0x00000000, + + // Write symbols's type argument if it is instantiated symbol + // eg. class C { p: T } <-- Show p as C.p here + // var a: C; + // var p = a.p; <--- Here p is property of C so show it as C.p instead of just C.p + WriteTypeParametersOrArguments = 0x00000001, + + // Use only external alias information to get the symbol name in the given context + // eg. module m { export class c { } } import x = m.c; + // When this flag is specified m.c will be used to refer to the class instead of alias symbol x + UseOnlyExternalAliasing = 0x00000002, + + // Build symbol name using any nodes needed, instead of just components of an entity name + AllowAnyNodeKind = 0x00000004, + + // Prefer aliases which are not directly visible + UseAliasDefinedOutsideCurrentScope = 0x00000008, + + // Skip building an accessible symbol chain + /* @internal */ DoNotIncludeSymbolChain = 0x00000010, +} + +/* @internal */ +export interface SymbolWalker { + /** Note: Return values are not ordered. */ + walkType(root: Type): { visitedTypes: ReadonlyArray, visitedSymbols: ReadonlyArray }; + /** Note: Return values are not ordered. */ + walkSymbol(root: Symbol): { visitedTypes: ReadonlyArray, visitedSymbols: ReadonlyArray }; +} + +// This was previously deprecated in our public API, but is still used internally +/* @internal */ +interface SymbolWriter extends SymbolTracker { + writeKeyword(text: string): void; + writeOperator(text: string): void; + writePunctuation(text: string): void; + writeSpace(text: string): void; + writeStringLiteral(text: string): void; + writeParameter(text: string): void; + writeProperty(text: string): void; + writeSymbol(text: string, symbol: Symbol): void; + writeLine(): void; + increaseIndent(): void; + decreaseIndent(): void; + clear(): void; +} + +/* @internal */ +export const enum SymbolAccessibility { + Accessible, + NotAccessible, + CannotBeNamed +} + +/* @internal */ +export const enum SyntheticSymbolKind { + UnionOrIntersection, + Spread +} + +export const enum TypePredicateKind { + This, + Identifier +} + +export interface TypePredicateBase { + kind: TypePredicateKind; + type: Type; +} + +export interface ThisTypePredicate extends TypePredicateBase { + kind: TypePredicateKind.This; +} + +export interface IdentifierTypePredicate extends TypePredicateBase { + kind: TypePredicateKind.Identifier; + parameterName: string; + parameterIndex: number; +} + +export type TypePredicate = IdentifierTypePredicate | ThisTypePredicate; + +/* @internal */ +export type AnyImportSyntax = ImportDeclaration | ImportEqualsDeclaration; + +/* @internal */ +export type AnyImportOrReExport = AnyImportSyntax | ExportDeclaration; + +/* @internal */ +export interface ValidImportTypeNode extends ImportTypeNode { + argument: LiteralTypeNode & { literal: StringLiteral }; +} + +/* @internal */ +export type AnyValidImportOrReExport = + | (ImportDeclaration | ExportDeclaration) & { moduleSpecifier: StringLiteral } + | ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteral } } + | RequireOrImportCall + | ValidImportTypeNode; + +/* @internal */ +export type RequireOrImportCall = CallExpression & { arguments: [StringLiteralLike] }; + +/* @internal */ +export type LateVisibilityPaintedStatement = + | AnyImportSyntax + | VariableStatement + | ClassDeclaration + | FunctionDeclaration + | ModuleDeclaration + | TypeAliasDeclaration + | InterfaceDeclaration + | EnumDeclaration; + +/* @internal */ +export interface SymbolVisibilityResult { + accessibility: SymbolAccessibility; + aliasesToMakeVisible?: LateVisibilityPaintedStatement[]; // aliases that need to have this symbol visible + errorSymbolName?: string; // Optional symbol name that results in error + errorNode?: Node; // optional node that results in error +} + +/* @internal */ +export interface SymbolAccessibilityResult extends SymbolVisibilityResult { + errorModuleName?: string; // If the symbol is not visible from module, module's name +} + +/* @internal */ +export interface AllAccessorDeclarations { + firstAccessor: AccessorDeclaration; + secondAccessor: AccessorDeclaration | undefined; + getAccessor: GetAccessorDeclaration | undefined; + setAccessor: SetAccessorDeclaration | undefined; +} + +/** Indicates how to serialize the name for a TypeReferenceNode when emitting decorator metadata */ +/* @internal */ +export enum TypeReferenceSerializationKind { + Unknown, // The TypeReferenceNode could not be resolved. The type name + // should be emitted using a safe fallback. + TypeWithConstructSignatureAndValue, // The TypeReferenceNode resolves to a type with a constructor + // function that can be reached at runtime (e.g. a `class` + // declaration or a `var` declaration for the static side + // of a type, such as the global `Promise` type in lib.d.ts). + VoidNullableOrNeverType, // The TypeReferenceNode resolves to a Void-like, Nullable, or Never type. + NumberLikeType, // The TypeReferenceNode resolves to a Number-like type. + StringLikeType, // The TypeReferenceNode resolves to a String-like type. + BooleanType, // The TypeReferenceNode resolves to a Boolean-like type. + ArrayLikeType, // The TypeReferenceNode resolves to an Array-like type. + ESSymbolType, // The TypeReferenceNode resolves to the ESSymbol type. + Promise, // The TypeReferenceNode resolved to the global Promise constructor symbol. + TypeWithCallSignature, // The TypeReferenceNode resolves to a Function type or a type + // with call signatures. + ObjectType, // The TypeReferenceNode resolves to any other type. +} + +/* @internal */ +export interface EmitResolver { + hasGlobalName(name: string): boolean; + getReferencedExportContainer(node: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration | undefined; + getReferencedImportDeclaration(node: Identifier): Declaration | undefined; + getReferencedDeclarationWithCollidingName(node: Identifier): Declaration | undefined; + isDeclarationWithCollidingName(node: Declaration): boolean; + isValueAliasDeclaration(node: Node): boolean; + isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean; + isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean; + getNodeCheckFlags(node: Node): NodeCheckFlags; + isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean; + isLateBound(node: Declaration): node is LateBoundDeclaration; + collectLinkedAliases(node: Identifier, setVisibility?: boolean): Node[] | undefined; + isImplementationOfOverload(node: FunctionLike): boolean | undefined; + isRequiredInitializedParameter(node: ParameterDeclaration): boolean; + isOptionalUninitializedParameterProperty(node: ParameterDeclaration): boolean; + isExpandoFunctionDeclaration(node: FunctionDeclaration): boolean; + getPropertiesOfContainerFunction(node: Declaration): Symbol[]; + createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined; + createReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined; + createTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined; + createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): Expression; + isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags | undefined, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult; + isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult; + // Returns the constant value this property access resolves to, or 'undefined' for a non-constant + getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; + getReferencedValueDeclaration(reference: Identifier): Declaration | undefined; + getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind; + isOptionalParameter(node: ParameterDeclaration): boolean; + moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean; + isArgumentsLocalBinding(node: Identifier): boolean; + getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): SourceFile | undefined; + getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): string[] | undefined; + getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] | undefined; + isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean; + getJsxFactoryEntity(location?: Node): EntityName | undefined; + getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations; + getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined; + isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean; +} + +/* @internal */ +export interface SymbolLinks { + immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead. + target?: Symbol; // Resolved (non-alias) target of an alias + type?: Type; // Type of value symbol + uniqueESSymbolType?: Type; // UniqueESSymbol type for a symbol + declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter + resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference + typeParameters?: TypeParameter[]; // Type parameters of type alias (undefined if non-generic) + outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type + inferredClassType?: Type; // Type of an inferred ES5 class + instantiations?: Map; // Instantiations of generic type alias (undefined if non-generic) + mapper?: TypeMapper; // Type mapper for instantiation alias + referenced?: boolean; // True if alias symbol has been referenced as a value + containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property + leftSpread?: Symbol; // Left source for synthetic spread property + rightSpread?: Symbol; // Right source for synthetic spread property + syntheticOrigin?: Symbol; // For a property on a mapped or spread type, points back to the original property + isDiscriminantProperty?: boolean; // True if discriminant synthetic property + resolvedExports?: SymbolTable; // Resolved exports of module or combined early- and late-bound static members of a class. + resolvedMembers?: SymbolTable; // Combined early- and late-bound members of a symbol + exportsChecked?: boolean; // True if exports of external module have been checked + typeParametersChecked?: boolean; // True if type parameters of merged class and interface declarations have been checked. + isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration + bindingElement?: BindingElement; // Binding element associated with property symbol + exportsSomeValue?: boolean; // True if module exports some value (not just types) + enumKind?: EnumKind; // Enum declaration classification + originatingImport?: ImportDeclaration | ImportCall; // Import declaration which produced the symbol, present if the symbol is marked as uncallable but had call signatures in `resolveESModuleSymbol` + lateSymbol?: Symbol; // Late-bound symbol for a computed property + specifierCache?: Map; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings +} + +/* @internal */ +export const enum EnumKind { + Numeric, // Numeric enum (each member has a TypeFlags.Enum type) + Literal // Literal enum (each member has a TypeFlags.EnumLiteral type) +} + +/* @internal */ +export const enum CheckFlags { + Instantiated = 1 << 0, // Instantiated symbol + SyntheticProperty = 1 << 1, // Property in union or intersection type + SyntheticMethod = 1 << 2, // Method in union or intersection type + Readonly = 1 << 3, // Readonly transient symbol + Partial = 1 << 4, // Synthetic property present in some but not all constituents + HasNonUniformType = 1 << 5, // Synthetic property with non-uniform type in constituents + ContainsPublic = 1 << 6, // Synthetic property with public constituent(s) + ContainsProtected = 1 << 7, // Synthetic property with protected constituent(s) + ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s) + ContainsStatic = 1 << 9, // Synthetic property with static constituent(s) + Late = 1 << 10, // Late-bound symbol for a computed property with a dynamic name + ReverseMapped = 1 << 11, // Property of reverse-inferred homomorphic mapped type + OptionalParameter = 1 << 12, // Optional parameter + RestParameter = 1 << 13, // Rest parameter + Synthetic = SyntheticProperty | SyntheticMethod +} + +/* @internal */ +export interface TransientSymbol extends Symbol, SymbolLinks { + checkFlags: CheckFlags; +} + +/* @internal */ +export interface ReverseMappedSymbol extends TransientSymbol { + propertyType: Type; + mappedType: MappedType; + constraintType: IndexType; +} + +export const enum InternalSymbolName { + Call = "__call", // Call signatures + Constructor = "__constructor", // Constructor implementations + New = "__new", // Constructor signatures + Index = "__index", // Index signatures + ExportStar = "__export", // Module export * declarations + Global = "__global", // Global self-reference + Missing = "__missing", // Indicates missing symbol + Type = "__type", // Anonymous type literal symbol + Object = "__object", // Anonymous object literal declaration + JSXAttributes = "__jsxAttributes", // Anonymous JSX attributes object literal declaration + Class = "__class", // Unnamed class expression + Function = "__function", // Unnamed function expression + Computed = "__computed", // Computed property name declaration with dynamic name + Resolving = "__resolving__", // Indicator symbol used to mark partially resolved type aliases + ExportEquals = "export=", // Export assignment symbol + Default = "default", // Default export symbol (technically not wholly internal, but included here for usability) + This = "this", +} + +/** + * This represents a string whose leading underscore have been escaped by adding extra leading underscores. + * The shape of this brand is rather unique compared to others we've used. + * Instead of just an intersection of a string and an object, it is that union-ed + * with an intersection of void and an object. This makes it wholly incompatible + * with a normal string (which is good, it cannot be misused on assignment or on usage), + * while still being comparable with a normal string via === (also good) and castable from a string. + */ +export type __String = (string & { __escapedIdentifier: void }) | (void & { __escapedIdentifier: void }) | InternalSymbolName; + +/** ReadonlyMap where keys are `__String`s. */ +export interface ReadonlyUnderscoreEscapedMap { + get(key: __String): T | undefined; + has(key: __String): boolean; + forEach(action: (value: T, key: __String) => void): void; + readonly size: number; + keys(): Iterator<__String>; + values(): Iterator; + entries(): Iterator<[__String, T]>; +} + +/** Map where keys are `__String`s. */ +export interface UnderscoreEscapedMap extends ReadonlyUnderscoreEscapedMap { + set(key: __String, value: T): this; + delete(key: __String): boolean; + clear(): void; +} + +/** SymbolTable based on ES6 Map interface. */ +export type SymbolTable = UnderscoreEscapedMap; + +/** Used to track a `declare module "foo*"`-like declaration. */ +/* @internal */ +export interface PatternAmbientModule { + pattern: Pattern; + symbol: Symbol; +} + +/* @internal */ +export const enum NodeCheckFlags { + TypeChecked = 0x00000001, // Node has been type checked + LexicalThis = 0x00000002, // Lexical 'this' reference + CaptureThis = 0x00000004, // Lexical 'this' used in body + CaptureNewTarget = 0x00000008, // Lexical 'new.target' used in body + SuperInstance = 0x00000100, // Instance 'super' reference + SuperStatic = 0x00000200, // Static 'super' reference + ContextChecked = 0x00000400, // Contextual types have been assigned + AsyncMethodWithSuper = 0x00000800, // An async method that reads a value from a member of 'super'. + AsyncMethodWithSuperBinding = 0x00001000, // An async method that assigns a value to a member of 'super'. + CaptureArguments = 0x00002000, // Lexical 'arguments' used in body + EnumValuesComputed = 0x00004000, // Values for enum members have been computed, and any errors have been reported for them. + LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration. + LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure + ContainsCapturedBlockScopeBinding = 0x00020000, // Part of a loop that contains block scoped variable captured in closure + CapturedBlockScopedBinding = 0x00040000, // Block-scoped binding that is captured in some function + BlockScopedBindingInLoop = 0x00080000, // Block-scoped binding with declaration nested inside iteration statement + ClassWithBodyScopedClassBinding = 0x00100000, // Decorated class that contains a binding to itself inside of the class body. + BodyScopedClassBinding = 0x00200000, // Binding to a decorated class inside of the class's body. + NeedsLoopOutParameter = 0x00400000, // Block scoped binding whose value should be explicitly copied outside of the converted loop + AssignmentsMarked = 0x00800000, // Parameter assignments have been marked + ClassWithConstructorReference = 0x01000000, // Class that contains a binding to its constructor inside of the class body. + ConstructorReferenceInClass = 0x02000000, // Binding to a class constructor inside of the class's body. +} + +/* @internal */ +export interface NodeLinks { + flags: NodeCheckFlags; // Set of flags specific to Node + resolvedType?: Type; // Cached type of type node + resolvedEnumType?: Type; // Cached constraint type from enum jsdoc tag + resolvedSignature?: Signature; // Cached signature of signature node or call expression + resolvedSymbol?: Symbol; // Cached name resolution result + resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result + maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate + enumMemberValue?: string | number; // Constant value of enum member + isVisible?: boolean; // Is this node visible + containsArgumentsReference?: boolean; // Whether a function-like declaration contains an 'arguments' reference + hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context + jsxFlags: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with + resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element + resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element + hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. + superCall?: SuperCall; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing + switchTypes?: Type[]; // Cached array of switch case expression types + jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node + contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive + deferredNodes?: Map; // Set of nodes whose checking has been deferred + capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement +} + +export const enum TypeFlags { + Any = 1 << 0, + Unknown = 1 << 1, + String = 1 << 2, + Number = 1 << 3, + Boolean = 1 << 4, + Enum = 1 << 5, + StringLiteral = 1 << 6, + NumberLiteral = 1 << 7, + BooleanLiteral = 1 << 8, + EnumLiteral = 1 << 9, // Always combined with StringLiteral, NumberLiteral, or Union + ESSymbol = 1 << 10, // Type of symbol primitive introduced in ES6 + UniqueESSymbol = 1 << 11, // unique symbol + Void = 1 << 12, + Undefined = 1 << 13, + Null = 1 << 14, + Never = 1 << 15, // Never type + TypeParameter = 1 << 16, // Type parameter + Object = 1 << 17, // Object type + Union = 1 << 18, // Union (T | U) + Intersection = 1 << 19, // Intersection (T & U) + Index = 1 << 20, // keyof T + IndexedAccess = 1 << 21, // T[K] + Conditional = 1 << 22, // T extends U ? X : Y + Substitution = 1 << 23, // Type parameter substitution + NonPrimitive = 1 << 24, // intrinsic object type + /* @internal */ + FreshLiteral = 1 << 25, // Fresh literal or unique type + /* @internal */ + UnionOfPrimitiveTypes = 1 << 26, // Type is union of primitive types + /* @internal */ + ContainsWideningType = 1 << 27, // Type is or contains undefined or null widening type + /* @internal */ + ContainsObjectLiteral = 1 << 28, // Type is or contains object literal type + /* @internal */ + ContainsAnyFunctionType = 1 << 29, // Type is or contains the anyFunctionType + + /* @internal */ + AnyOrUnknown = Any | Unknown, + /* @internal */ + Nullable = Undefined | Null, + Literal = StringLiteral | NumberLiteral | BooleanLiteral, + Unit = Literal | UniqueESSymbol | Nullable, + StringOrNumberLiteral = StringLiteral | NumberLiteral, + /* @internal */ + StringOrNumberLiteralOrUnique = StringLiteral | NumberLiteral | UniqueESSymbol, + /* @internal */ + DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null, + PossiblyFalsy = DefinitelyFalsy | String | Number | Boolean, + /* @internal */ + Intrinsic = Any | Unknown | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive, + /* @internal */ + Primitive = String | Number | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal | UniqueESSymbol, + StringLike = String | StringLiteral, + NumberLike = Number | NumberLiteral | Enum, + BooleanLike = Boolean | BooleanLiteral, + EnumLike = Enum | EnumLiteral, + ESSymbolLike = ESSymbol | UniqueESSymbol, + VoidLike = Void | Undefined, + /* @internal */ + DisjointDomains = NonPrimitive | StringLike | NumberLike | BooleanLike | ESSymbolLike | VoidLike | Null, + UnionOrIntersection = Union | Intersection, + StructuredType = Object | Union | Intersection, + TypeVariable = TypeParameter | IndexedAccess, + InstantiableNonPrimitive = TypeVariable | Conditional | Substitution, + InstantiablePrimitive = Index, + Instantiable = InstantiableNonPrimitive | InstantiablePrimitive, + StructuredOrInstantiable = StructuredType | Instantiable, + + // 'Narrowable' types are types where narrowing actually narrows. + // This *should* be every type other than null, undefined, void, and never + Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive, + NotUnionOrUnit = Any | Unknown | ESSymbol | Object | NonPrimitive, + /* @internal */ + NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable, + /* @internal */ + RequiresWidening = ContainsWideningType | ContainsObjectLiteral, + /* @internal */ + PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | ContainsAnyFunctionType, + // The following flags are used for different purposes during union and intersection type construction + /* @internal */ + NonWideningType = ContainsWideningType, + /* @internal */ + Wildcard = ContainsObjectLiteral, + /* @internal */ + EmptyObject = ContainsAnyFunctionType, + /* @internal */ + ConstructionFlags = NonWideningType | Wildcard | EmptyObject, + // The following flag is used for different purposes by maybeTypeOfKind + /* @internal */ + GenericMappedType = ContainsWideningType +} + +export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; + +// Properties common to all types +export interface Type { + flags: TypeFlags; // Flags + /* @internal */ id: number; // Unique ID + /* @internal */ checker: TypeChecker; + symbol: Symbol; // Symbol associated with type (if any) + pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any) + aliasSymbol?: Symbol; // Alias associated with type + aliasTypeArguments?: ReadonlyArray; // Alias type arguments (if any) + /* @internal */ + wildcardInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type + /* @internal */ + immediateBaseConstraint?: Type; // Immediate base constraint cache +} + +/* @internal */ +// Intrinsic types (TypeFlags.Intrinsic) +export interface IntrinsicType extends Type { + intrinsicName: string; // Name of intrinsic type +} + +/* @internal */ +export interface FreshableIntrinsicType extends IntrinsicType { + freshType: IntrinsicType; // Fresh version of type + regularType: IntrinsicType; // Regular version of type +} + +/* @internal */ +export type FreshableType = LiteralType | FreshableIntrinsicType; + +// String literal types (TypeFlags.StringLiteral) +// Numeric literal types (TypeFlags.NumberLiteral) +export interface LiteralType extends Type { + value: string | number; // Value of literal + freshType: LiteralType; // Fresh version of type + regularType: LiteralType; // Regular version of type +} + +// Unique symbol types (TypeFlags.UniqueESSymbol) +export interface UniqueESSymbolType extends Type { + symbol: Symbol; +} + +export interface StringLiteralType extends LiteralType { + value: string; +} + +export interface NumberLiteralType extends LiteralType { + value: number; +} + +// Enum types (TypeFlags.Enum) +export interface EnumType extends Type { +} + +export const enum ObjectFlags { + Class = 1 << 0, // Class + Interface = 1 << 1, // Interface + Reference = 1 << 2, // Generic type reference + Tuple = 1 << 3, // Synthesized generic tuple type + Anonymous = 1 << 4, // Anonymous + Mapped = 1 << 5, // Mapped + Instantiated = 1 << 6, // Instantiated anonymous or mapped type + ObjectLiteral = 1 << 7, // Originates in an object literal + EvolvingArray = 1 << 8, // Evolving array type + ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties + ContainsSpread = 1 << 10, // Object literal contains spread operation + ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type + JsxAttributes = 1 << 12, // Jsx attributes type + MarkerType = 1 << 13, // Marker type used for variance probing + JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members + ClassOrInterface = Class | Interface +} + +// Object types (TypeFlags.ObjectType) +export interface ObjectType extends Type { + objectFlags: ObjectFlags; + /* @internal */ members?: SymbolTable; // Properties by name + /* @internal */ properties?: Symbol[]; // Properties + /* @internal */ callSignatures?: ReadonlyArray; // Call signatures of type + /* @internal */ constructSignatures?: ReadonlyArray; // Construct signatures of type + /* @internal */ stringIndexInfo?: IndexInfo; // String indexing info + /* @internal */ numberIndexInfo?: IndexInfo; // Numeric indexing info +} + +/** Class and interface types (ObjectFlags.Class and ObjectFlags.Interface). */ +export interface InterfaceType extends ObjectType { + typeParameters: TypeParameter[] | undefined; // Type parameters (undefined if non-generic) + outerTypeParameters: TypeParameter[] | undefined; // Outer type parameters (undefined if none) + localTypeParameters: TypeParameter[] | undefined; // Local type parameters (undefined if none) + thisType: TypeParameter | undefined; // The "this" type (undefined if none) + /* @internal */ + resolvedBaseConstructorType?: Type; // Resolved base constructor type of class + /* @internal */ + resolvedBaseTypes: BaseType[]; // Resolved base types +} + +// Object type or intersection of object types +export type BaseType = ObjectType | IntersectionType; + +export interface InterfaceTypeWithDeclaredMembers extends InterfaceType { + declaredProperties: Symbol[]; // Declared members + declaredCallSignatures: Signature[]; // Declared call signatures + declaredConstructSignatures: Signature[]; // Declared construct signatures + declaredStringIndexInfo?: IndexInfo; // Declared string indexing info + declaredNumberIndexInfo?: IndexInfo; // Declared numeric indexing info +} + +/** + * Type references (ObjectFlags.Reference). When a class or interface has type parameters or + * a "this" type, references to the class or interface are made using type references. The + * typeArguments property specifies the types to substitute for the type parameters of the + * class or interface and optionally includes an extra element that specifies the type to + * substitute for "this" in the resulting instantiation. When no extra argument is present, + * the type reference itself is substituted for "this". The typeArguments property is undefined + * if the class or interface has no type parameters and the reference isn't specifying an + * explicit "this" argument. + */ +export interface TypeReference extends ObjectType { + target: GenericType; // Type reference target + typeArguments?: ReadonlyArray; // Type reference type arguments (undefined if none) +} + +/* @internal */ +export const enum Variance { + Invariant = 0, // Neither covariant nor contravariant + Covariant = 1, // Covariant + Contravariant = 2, // Contravariant + Bivariant = 3, // Both covariant and contravariant + Independent = 4, // Unwitnessed type parameter +} + +// Generic class and interface types +export interface GenericType extends InterfaceType, TypeReference { + /* @internal */ + instantiations: Map; // Generic instantiation cache + /* @internal */ + variances?: Variance[]; // Variance of each type parameter +} + +export interface TupleType extends GenericType { + minLength: number; + hasRestElement: boolean; + associatedNames?: __String[]; +} + +export interface TupleTypeReference extends TypeReference { + target: TupleType; +} + +export interface UnionOrIntersectionType extends Type { + types: Type[]; // Constituent types + /* @internal */ + propertyCache: SymbolTable; // Cache of resolved properties + /* @internal */ + resolvedProperties: Symbol[]; + /* @internal */ + resolvedIndexType: IndexType; + /* @internal */ + resolvedStringIndexType: IndexType; + /* @internal */ + resolvedBaseConstraint: Type; + /* @internal */ + couldContainTypeVariables: boolean; +} + +export interface UnionType extends UnionOrIntersectionType { } + +export interface IntersectionType extends UnionOrIntersectionType { + /* @internal */ + resolvedApparentType: Type; +} + +export type StructuredType = ObjectType | UnionType | IntersectionType; + +/* @internal */ +// An instantiated anonymous type has a target and a mapper +export interface AnonymousType extends ObjectType { + target?: AnonymousType; // Instantiation target + mapper?: TypeMapper; // Instantiation mapper +} + +/* @internal */ +export interface MappedType extends AnonymousType { + declaration: MappedTypeNode; + typeParameter?: TypeParameter; + constraintType?: Type; + templateType?: Type; + modifiersType?: Type; + resolvedApparentType?: Type; + instantiating?: boolean; +} + +export interface EvolvingArrayType extends ObjectType { + elementType: Type; // Element expressions of evolving array type + finalArrayType?: Type; // Final array type of evolving array type +} + +/* @internal */ +export interface ReverseMappedType extends ObjectType { + source: Type; + mappedType: MappedType; + constraintType: IndexType; +} + +/* @internal */ +// Resolved object, union, or intersection type +export interface ResolvedType extends ObjectType, UnionOrIntersectionType { + members: SymbolTable; // Properties by name + properties: Symbol[]; // Properties + callSignatures: ReadonlyArray; // Call signatures of type + constructSignatures: ReadonlyArray; // Construct signatures of type +} + +/* @internal */ +// Object literals are initially marked fresh. Freshness disappears following an assignment, +// before a type assertion, or when an object literal's type is widened. The regular +// version of a fresh type is identical except for the TypeFlags.FreshObjectLiteral flag. +export interface FreshObjectLiteralType extends ResolvedType { + regularType: ResolvedType; // Regular version of fresh type +} + +// Just a place to cache element types of iterables and iterators +/* @internal */ +export interface IterableOrIteratorType extends ObjectType, UnionType { + iteratedTypeOfIterable?: Type; + iteratedTypeOfIterator?: Type; + iteratedTypeOfAsyncIterable?: Type; + iteratedTypeOfAsyncIterator?: Type; +} + +/* @internal */ +export interface PromiseOrAwaitableType extends ObjectType, UnionType { + promiseTypeOfPromiseConstructor?: Type; + promisedTypeOfPromise?: Type; + awaitedTypeOfType?: Type; +} + +/* @internal */ +export interface SyntheticDefaultModuleType extends Type { + syntheticType?: Type; +} + +export interface InstantiableType extends Type { + /* @internal */ + resolvedBaseConstraint?: Type; + /* @internal */ + resolvedIndexType?: IndexType; + /* @internal */ + resolvedStringIndexType?: IndexType; +} + +// Type parameters (TypeFlags.TypeParameter) +export interface TypeParameter extends InstantiableType { + /** Retrieve using getConstraintFromTypeParameter */ + /* @internal */ + constraint?: Type; // Constraint + /* @internal */ + default?: Type; + /* @internal */ + target?: TypeParameter; // Instantiation target + /* @internal */ + mapper?: TypeMapper; // Instantiation mapper + /* @internal */ + isThisType?: boolean; + /* @internal */ + resolvedDefaultType?: Type; +} + +// Indexed access types (TypeFlags.IndexedAccess) +// Possible forms are T[xxx], xxx[T], or xxx[keyof T], where T is a type variable +export interface IndexedAccessType extends InstantiableType { + objectType: Type; + indexType: Type; + constraint?: Type; + simplified?: Type; +} + +export type TypeVariable = TypeParameter | IndexedAccessType; + +// keyof T types (TypeFlags.Index) +export interface IndexType extends InstantiableType { + type: InstantiableType | UnionOrIntersectionType; + /* @internal */ + stringsOnly: boolean; +} + +export interface ConditionalRoot { + node: ConditionalTypeNode; + checkType: Type; + extendsType: Type; + trueType: Type; + falseType: Type; + isDistributive: boolean; + inferTypeParameters?: TypeParameter[]; + outerTypeParameters?: TypeParameter[]; + instantiations?: Map; + aliasSymbol?: Symbol; + aliasTypeArguments?: Type[]; +} + +// T extends U ? X : Y (TypeFlags.Conditional) +export interface ConditionalType extends InstantiableType { + root: ConditionalRoot; + checkType: Type; + extendsType: Type; + resolvedTrueType?: Type; + resolvedFalseType?: Type; + /* @internal */ + resolvedDefaultConstraint?: Type; + /* @internal */ + mapper?: TypeMapper; + /* @internal */ + combinedMapper?: TypeMapper; +} + +// Type parameter substitution (TypeFlags.Substitution) +// Substitution types are created for type parameters or indexed access types that occur in the +// true branch of a conditional type. For example, in 'T extends string ? Foo : Bar', the +// reference to T in Foo is resolved as a substitution type that substitutes 'string & T' for T. +// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution +// types disappear upon instantiation (just like type parameters). +export interface SubstitutionType extends InstantiableType { + typeVariable: TypeVariable; // Target type variable + substitute: Type; // Type to substitute for type parameter +} + +export const enum SignatureKind { + Call, + Construct, +} + +export interface Signature { + declaration?: SignatureDeclaration | JSDocSignature; // Originating declaration + typeParameters?: ReadonlyArray; // Type parameters (undefined if non-generic) + parameters: ReadonlyArray; // Parameters + /* @internal */ + thisParameter?: Symbol; // symbol of this-type parameter + /* @internal */ + // See comment in `instantiateSignature` for why these are set lazily. + resolvedReturnType?: Type; // Lazily set by `getReturnTypeOfSignature`. + /* @internal */ + // Lazily set by `getTypePredicateOfSignature`. + // `undefined` indicates a type predicate that has not yet been computed. + // Uses a special `noTypePredicate` sentinel value to indicate that there is no type predicate. This looks like a TypePredicate at runtime to avoid polymorphism. + resolvedTypePredicate?: TypePredicate; + /* @internal */ + minArgumentCount: number; // Number of non-optional parameters + /* @internal */ + hasRestParameter: boolean; // True if last parameter is rest parameter + /* @internal */ + hasLiteralTypes: boolean; // True if specialized + /* @internal */ + target?: Signature; // Instantiation target + /* @internal */ + mapper?: TypeMapper; // Instantiation mapper + /* @internal */ + unionSignatures?: Signature[]; // Underlying signatures of a union signature + /* @internal */ + erasedSignatureCache?: Signature; // Erased version of signature (deferred) + /* @internal */ + canonicalSignatureCache?: Signature; // Canonical version of signature (deferred) + /* @internal */ + isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison + /* @internal */ + instantiations?: Map; // Generic signature instantiation cache +} + +export const enum IndexKind { + String, + Number, +} + +export interface IndexInfo { + type: Type; + isReadonly: boolean; + declaration?: IndexSignatureDeclaration; +} + +/* @internal */ +export type TypeMapper = (t: TypeParameter) => Type; + +export const enum InferencePriority { + NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type + HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type + MappedTypeConstraint = 1 << 2, // Reverse inference for mapped type + ReturnType = 1 << 3, // Inference made from return type of generic function + LiteralKeyof = 1 << 4, // Inference made from a string literal to a keyof T + NoConstraints = 1 << 5, // Don't infer from constraints of instantiable types + AlwaysStrict = 1 << 6, // Always use strict rules for contravariant inferences + + PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates +} + +/* @internal */ +export interface InferenceInfo { + typeParameter: TypeParameter; // Type parameter for which inferences are being made + candidates: Type[] | undefined; // Candidates in covariant positions (or undefined) + contraCandidates: Type[] | undefined; // Candidates in contravariant positions (or undefined) + inferredType?: Type; // Cache for resolved inferred type + priority?: InferencePriority; // Priority of current inference set + topLevel: boolean; // True if all inferences are to top level occurrences + isFixed: boolean; // True if inferences are fixed +} + +/* @internal */ +export const enum InferenceFlags { + None = 0, // No special inference behaviors + NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType) + AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType) +} + +/** + * Ternary values are defined such that + * x & y is False if either x or y is False. + * x & y is Maybe if either x or y is Maybe, but neither x or y is False. + * x & y is True if both x and y are True. + * x | y is False if both x and y are False. + * x | y is Maybe if either x or y is Maybe, but neither x or y is True. + * x | y is True if either x or y is True. + */ +/* @internal */ +export const enum Ternary { + False = 0, + Maybe = 1, + True = -1 +} + +/* @internal */ +export type TypeComparer = (s: Type, t: Type, reportErrors?: boolean) => Ternary; + +/* @internal */ +export interface InferenceContext extends TypeMapper { + typeParameters: ReadonlyArray; // Type parameters for which inferences are made + signature?: Signature; // Generic signature for which inferences are made (if any) + inferences: InferenceInfo[]; // Inferences made for each type parameter + flags: InferenceFlags; // Inference flags + compareTypes: TypeComparer; // Type comparer function +} + +/* @internal */ +export interface WideningContext { + parent?: WideningContext; // Parent context + propertyName?: __String; // Name of property in parent + siblings?: Type[]; // Types of siblings + resolvedProperties?: Symbol[]; // Properties occurring in sibling object literals +} + +/* @internal */ +export const enum AssignmentDeclarationKind { + None, + /// exports.name = expr + ExportsProperty, + /// module.exports = expr + ModuleExports, + /// className.prototype.name = expr + PrototypeProperty, + /// this.name = expr + ThisProperty, + // F.name = expr + Property, + // F.prototype = { ... } + Prototype, + // Object.defineProperty(x, 'name', { value: any, writable?: boolean (false by default) }); + // Object.defineProperty(x, 'name', { get: Function, set: Function }); + // Object.defineProperty(x, 'name', { get: Function }); + // Object.defineProperty(x, 'name', { set: Function }); + ObjectDefinePropertyValue, + // Object.defineProperty(exports || module.exports, 'name', ...); + ObjectDefinePropertyExports, + // Object.defineProperty(Foo.prototype, 'name', ...); + ObjectDefinePrototypeProperty, +} + +/** @deprecated Use FileExtensionInfo instead. */ +export type JsFileExtensionInfo = FileExtensionInfo; + +export interface FileExtensionInfo { + extension: string; + isMixedContent: boolean; + scriptKind?: ScriptKind; +} + +export interface DiagnosticMessage { + key: string; + category: DiagnosticCategory; + code: number; + message: string; + reportsUnnecessary?: {}; +} + +/** + * A linked list of formatted diagnostic messages to be used as part of a multiline message. + * It is built from the bottom up, leaving the head to be the "main" diagnostic. + * While it seems that DiagnosticMessageChain is structurally similar to DiagnosticMessage, + * the difference is that messages are all preformatted in DMC. + */ +export interface DiagnosticMessageChain { + messageText: string; + category: DiagnosticCategory; + code: number; + next?: DiagnosticMessageChain; +} + +export interface Diagnostic extends DiagnosticRelatedInformation { + /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ + reportsUnnecessary?: {}; + source?: string; + relatedInformation?: DiagnosticRelatedInformation[]; +} +export interface DiagnosticRelatedInformation { + category: DiagnosticCategory; + code: number; + file: SourceFile | undefined; + start: number | undefined; + length: number | undefined; + messageText: string | DiagnosticMessageChain; +} +export interface DiagnosticWithLocation extends Diagnostic { + file: SourceFile; + start: number; + length: number; +} + +export enum DiagnosticCategory { + Warning, + Error, + Suggestion, + Message +} +/* @internal */ +export function diagnosticCategoryName(d: { category: DiagnosticCategory }, lowerCase = true): string { + const name = DiagnosticCategory[d.category]; + return lowerCase ? name.toLowerCase() : name; +} + +export enum ModuleResolutionKind { + Classic = 1, + NodeJs = 2 +} + +export interface PluginImport { + name: string; +} + +export interface ProjectReference { + /** A normalized path on disk */ + path: string; + /** The path as the user originally wrote it */ + originalPath?: string; + /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */ + prepend?: boolean; + /** True if it is intended that this reference form a circularity */ + circular?: boolean; +} + +export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; + +export interface CompilerOptions { + /*@internal*/ all?: boolean; + allowJs?: boolean; + /*@internal*/ allowNonTsExtensions?: boolean; + allowSyntheticDefaultImports?: boolean; + allowUnreachableCode?: boolean; + allowUnusedLabels?: boolean; + alwaysStrict?: boolean; // Always combine with strict property + baseUrl?: string; + /** An error if set - this should only go through the -b pipeline and not actually be observed */ + /*@internal*/ + build?: boolean; + charset?: string; + checkJs?: boolean; + /* @internal */ configFilePath?: string; + /** configFile is set as non enumerable property so as to avoid checking of json source files */ + /* @internal */ readonly configFile?: TsConfigSourceFile; + declaration?: boolean; + declarationMap?: boolean; + emitDeclarationOnly?: boolean; + declarationDir?: string; + /* @internal */ diagnostics?: boolean; + /* @internal */ extendedDiagnostics?: boolean; + disableSizeLimit?: boolean; + downlevelIteration?: boolean; + emitBOM?: boolean; + emitDecoratorMetadata?: boolean; + experimentalDecorators?: boolean; + forceConsistentCasingInFileNames?: boolean; + /*@internal*/help?: boolean; + importHelpers?: boolean; + /*@internal*/init?: boolean; + inlineSourceMap?: boolean; + inlineSources?: boolean; + isolatedModules?: boolean; + jsx?: JsxEmit; + keyofStringsOnly?: boolean; + lib?: string[]; + /*@internal*/listEmittedFiles?: boolean; + /*@internal*/listFiles?: boolean; + locale?: string; + mapRoot?: string; + maxNodeModuleJsDepth?: number; + module?: ModuleKind; + moduleResolution?: ModuleResolutionKind; + newLine?: NewLineKind; + noEmit?: boolean; + /*@internal*/noEmitForJsFiles?: boolean; + noEmitHelpers?: boolean; + noEmitOnError?: boolean; + noErrorTruncation?: boolean; + noFallthroughCasesInSwitch?: boolean; + noImplicitAny?: boolean; // Always combine with strict property + noImplicitReturns?: boolean; + noImplicitThis?: boolean; // Always combine with strict property + noStrictGenericChecks?: boolean; + noUnusedLocals?: boolean; + noUnusedParameters?: boolean; + noImplicitUseStrict?: boolean; + noLib?: boolean; + noResolve?: boolean; + out?: string; + outDir?: string; + outFile?: string; + paths?: MapLike; + /*@internal*/ plugins?: PluginImport[]; + preserveConstEnums?: boolean; + preserveSymlinks?: boolean; + /* @internal */ preserveWatchOutput?: boolean; + project?: string; + /* @internal */ pretty?: boolean; + reactNamespace?: string; + jsxFactory?: string; + composite?: boolean; + removeComments?: boolean; + rootDir?: string; + rootDirs?: string[]; + skipLibCheck?: boolean; + skipDefaultLibCheck?: boolean; + sourceMap?: boolean; + sourceRoot?: string; + strict?: boolean; + strictFunctionTypes?: boolean; // Always combine with strict property + strictBindCallApply?: boolean; // Always combine with strict property + strictNullChecks?: boolean; // Always combine with strict property + strictPropertyInitialization?: boolean; // Always combine with strict property + stripInternal?: boolean; + suppressExcessPropertyErrors?: boolean; + suppressImplicitAnyIndexErrors?: boolean; + /* @internal */ suppressOutputPathCheck?: boolean; + target?: ScriptTarget; // TODO: GH#18217 frequently asserted as defined + traceResolution?: boolean; + resolveJsonModule?: boolean; + types?: string[]; + /** Paths used to compute primary types search locations */ + typeRoots?: string[]; + /*@internal*/ version?: boolean; + /*@internal*/ watch?: boolean; + esModuleInterop?: boolean; + + [option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined; +} + +export interface TypeAcquisition { + /* @deprecated typingOptions.enableAutoDiscovery + * Use typeAcquisition.enable instead. + */ + enableAutoDiscovery?: boolean; + enable?: boolean; + include?: string[]; + exclude?: string[]; + [option: string]: string[] | boolean | undefined; +} + +export enum ModuleKind { + None = 0, + CommonJS = 1, + AMD = 2, + UMD = 3, + System = 4, + ES2015 = 5, + ESNext = 6 +} + +export const enum JsxEmit { + None = 0, + Preserve = 1, + React = 2, + ReactNative = 3 +} + +export const enum NewLineKind { + CarriageReturnLineFeed = 0, + LineFeed = 1 +} + +export interface LineAndCharacter { + /** 0-based. */ + line: number; + /* + * 0-based. This value denotes the character position in line and is different from the 'column' because of tab characters. + */ + character: number; +} + +export const enum ScriptKind { + Unknown = 0, + JS = 1, + JSX = 2, + TS = 3, + TSX = 4, + External = 5, + JSON = 6, + /** + * Used on extensions that doesn't define the ScriptKind but the content defines it. + * Deferred extensions are going to be included in all project contexts. + */ + Deferred = 7 +} + +export const enum ScriptTarget { + ES3 = 0, + ES5 = 1, + ES2015 = 2, + ES2016 = 3, + ES2017 = 4, + ES2018 = 5, + ESNext = 6, + JSON = 100, + Latest = ESNext, +} + +export const enum LanguageVariant { + Standard, + JSX +} + +/** Either a parsed command line or a parsed tsconfig.json */ +export interface ParsedCommandLine { + options: CompilerOptions; + typeAcquisition?: TypeAcquisition; + fileNames: string[]; + projectReferences?: ReadonlyArray; + raw?: any; + errors: Diagnostic[]; + wildcardDirectories?: MapLike; + compileOnSave?: boolean; + /* @internal */ configFileSpecs?: ConfigFileSpecs; +} + +export const enum WatchDirectoryFlags { + None = 0, + Recursive = 1 << 0, +} + +/* @internal */ +export interface ConfigFileSpecs { + filesSpecs: ReadonlyArray | undefined; + /** + * Present to report errors (user specified specs), validatedIncludeSpecs are used for file name matching + */ + includeSpecs?: ReadonlyArray; + /** + * Present to report errors (user specified specs), validatedExcludeSpecs are used for file name matching + */ + excludeSpecs?: ReadonlyArray; + validatedIncludeSpecs?: ReadonlyArray; + validatedExcludeSpecs?: ReadonlyArray; + wildcardDirectories: MapLike; +} + +export interface ExpandResult { + fileNames: string[]; + wildcardDirectories: MapLike; + /* @internal */ spec: ConfigFileSpecs; +} + +export interface CreateProgramOptions { + rootNames: ReadonlyArray; + options: CompilerOptions; + projectReferences?: ReadonlyArray; + host?: CompilerHost; + oldProgram?: Program; + configFileParsingDiagnostics?: ReadonlyArray; +} + +/* @internal */ +export interface CommandLineOptionBase { + name: string; + type: "string" | "number" | "boolean" | "object" | "list" | Map; // a value of a primitive type, or an object literal mapping named values to actual values + isFilePath?: boolean; // True if option value is a path or fileName + shortName?: string; // A short mnemonic for convenience - for instance, 'h' can be used in place of 'help' + description?: DiagnosticMessage; // The message describing what the command line switch does + paramType?: DiagnosticMessage; // The name to be used for a non-boolean option's parameter + isTSConfigOnly?: boolean; // True if option can only be specified via tsconfig.json file + isCommandLineOnly?: boolean; + showInSimplifiedHelpView?: boolean; + category?: DiagnosticMessage; + strictFlag?: true; // true if the option is one of the flag under strict + affectsSourceFile?: true; // true if we should recreate SourceFiles after this option changes + affectsModuleResolution?: true; // currently same effect as `affectsSourceFile` + affectsBindDiagnostics?: true; // true if this affects binding (currently same effect as `affectsSourceFile`) + affectsSemanticDiagnostics?: true; // true if option affects semantic diagnostics +} + +/* @internal */ +export interface CommandLineOptionOfPrimitiveType extends CommandLineOptionBase { + type: "string" | "number" | "boolean"; +} + +/* @internal */ +export interface CommandLineOptionOfCustomType extends CommandLineOptionBase { + type: Map; // an object literal mapping named values to actual values +} + +/* @internal */ +export interface TsConfigOnlyOption extends CommandLineOptionBase { + type: "object"; + elementOptions?: Map; + extraKeyDiagnosticMessage?: DiagnosticMessage; +} + +/* @internal */ +export interface CommandLineOptionOfListType extends CommandLineOptionBase { + type: "list"; + element: CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType | TsConfigOnlyOption; +} + +/* @internal */ +export type CommandLineOption = CommandLineOptionOfCustomType | CommandLineOptionOfPrimitiveType | TsConfigOnlyOption | CommandLineOptionOfListType; + +/* @internal */ +export const enum CharacterCodes { + nullCharacter = 0, + maxAsciiCharacter = 0x7F, + + lineFeed = 0x0A, // \n + carriageReturn = 0x0D, // \r + lineSeparator = 0x2028, + paragraphSeparator = 0x2029, + nextLine = 0x0085, + + // Unicode 3.0 space characters + space = 0x0020, // " " + nonBreakingSpace = 0x00A0, // + enQuad = 0x2000, + emQuad = 0x2001, + enSpace = 0x2002, + emSpace = 0x2003, + threePerEmSpace = 0x2004, + fourPerEmSpace = 0x2005, + sixPerEmSpace = 0x2006, + figureSpace = 0x2007, + punctuationSpace = 0x2008, + thinSpace = 0x2009, + hairSpace = 0x200A, + zeroWidthSpace = 0x200B, + narrowNoBreakSpace = 0x202F, + ideographicSpace = 0x3000, + mathematicalSpace = 0x205F, + ogham = 0x1680, + + _ = 0x5F, + $ = 0x24, + + _0 = 0x30, + _1 = 0x31, + _2 = 0x32, + _3 = 0x33, + _4 = 0x34, + _5 = 0x35, + _6 = 0x36, + _7 = 0x37, + _8 = 0x38, + _9 = 0x39, + + a = 0x61, + b = 0x62, + c = 0x63, + d = 0x64, + e = 0x65, + f = 0x66, + g = 0x67, + h = 0x68, + i = 0x69, + j = 0x6A, + k = 0x6B, + l = 0x6C, + m = 0x6D, + n = 0x6E, + o = 0x6F, + p = 0x70, + q = 0x71, + r = 0x72, + s = 0x73, + t = 0x74, + u = 0x75, + v = 0x76, + w = 0x77, + x = 0x78, + y = 0x79, + z = 0x7A, + + A = 0x41, + B = 0x42, + C = 0x43, + D = 0x44, + E = 0x45, + F = 0x46, + G = 0x47, + H = 0x48, + I = 0x49, + J = 0x4A, + K = 0x4B, + L = 0x4C, + M = 0x4D, + N = 0x4E, + O = 0x4F, + P = 0x50, + Q = 0x51, + R = 0x52, + S = 0x53, + T = 0x54, + U = 0x55, + V = 0x56, + W = 0x57, + X = 0x58, + Y = 0x59, + Z = 0x5a, + + ampersand = 0x26, // & + asterisk = 0x2A, // * + at = 0x40, // @ + backslash = 0x5C, // \ + backtick = 0x60, // ` + bar = 0x7C, // | + caret = 0x5E, // ^ + closeBrace = 0x7D, // } + closeBracket = 0x5D, // ] + closeParen = 0x29, // ) + colon = 0x3A, // : + comma = 0x2C, // , + dot = 0x2E, // . + doubleQuote = 0x22, // " + equals = 0x3D, // = + exclamation = 0x21, // ! + greaterThan = 0x3E, // > + hash = 0x23, // # + lessThan = 0x3C, // < + minus = 0x2D, // - + openBrace = 0x7B, // { + openBracket = 0x5B, // [ + openParen = 0x28, // ( + percent = 0x25, // % + plus = 0x2B, // + + question = 0x3F, // ? + semicolon = 0x3B, // ; + singleQuote = 0x27, // ' + slash = 0x2F, // / + tilde = 0x7E, // ~ + + backspace = 0x08, // \b + formFeed = 0x0C, // \f + byteOrderMark = 0xFEFF, + tab = 0x09, // \t + verticalTab = 0x0B, // \v +} + +export interface ModuleResolutionHost { + // TODO: GH#18217 Optional methods frequently used as non-optional + + fileExists(fileName: string): boolean; + // readFile function is used to read arbitrary text files on disk, i.e. when resolution procedure needs the content of 'package.json' + // to determine location of bundled typings for node module + readFile(fileName: string): string | undefined; + trace?(s: string): void; + directoryExists?(directoryName: string): boolean; + /** + * Resolve a symbolic link. + * @see https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_options + */ + realpath?(path: string): string; + getCurrentDirectory?(): string; + getDirectories?(path: string): string[]; +} + +/** + * Represents the result of module resolution. + * Module resolution will pick up tsx/jsx/js files even if '--jsx' and '--allowJs' are turned off. + * The Program will then filter results based on these flags. + * + * Prefer to return a `ResolvedModuleFull` so that the file type does not have to be inferred. + */ +export interface ResolvedModule { + /** Path of the file the module was resolved to. */ + resolvedFileName: string; + /** True if `resolvedFileName` comes from `node_modules`. */ + isExternalLibraryImport?: boolean; +} + +/** + * ResolvedModule with an explicitly provided `extension` property. + * Prefer this over `ResolvedModule`. + * If changing this, remember to change `moduleResolutionIsEqualTo`. + */ +export interface ResolvedModuleFull extends ResolvedModule { + /* @internal */ + readonly originalPath?: string; + /** + * Extension of resolvedFileName. This must match what's at the end of resolvedFileName. + * This is optional for backwards-compatibility, but will be added if not provided. + */ + extension: Extension; + packageId?: PackageId; +} + +/** + * Unique identifier with a package name and version. + * If changing this, remember to change `packageIdIsEqual`. + */ +export interface PackageId { + /** + * Name of the package. + * Should not include `@types`. + * If accessing a non-index file, this should include its name e.g. "foo/bar". + */ + name: string; + /** + * Name of a submodule within this package. + * May be "". + */ + subModuleName: string; + /** Version of the package, e.g. "1.2.3" */ + version: string; +} + +export const enum Extension { + Ts = ".ts", + Tsx = ".tsx", + Dts = ".d.ts", + Js = ".js", + Jsx = ".jsx", + Json = ".json" +} + +export interface ResolvedModuleWithFailedLookupLocations { + readonly resolvedModule: ResolvedModuleFull | undefined; + /* @internal */ + readonly failedLookupLocations: ReadonlyArray; +} + +export interface ResolvedTypeReferenceDirective { + // True if the type declaration file was found in a primary lookup location + primary: boolean; + // The location of the .d.ts file we located, or undefined if resolution failed + resolvedFileName: string | undefined; + packageId?: PackageId; + /** True if `resolvedFileName` comes from `node_modules`. */ + isExternalLibraryImport?: boolean; +} + +export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations { + readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; + readonly failedLookupLocations: ReadonlyArray; +} + +/* @internal */ +export type HasInvalidatedResolution = (sourceFile: Path) => boolean; + +export interface CompilerHost extends ModuleResolutionHost { + getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; + getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined; + getCancellationToken?(): CancellationToken; + getDefaultLibFileName(options: CompilerOptions): string; + getDefaultLibLocation?(): string; + writeFile: WriteFileCallback; + getCurrentDirectory(): string; + getDirectories(path: string): string[]; + getCanonicalFileName(fileName: string): string; + useCaseSensitiveFileNames(): boolean; + getNewLine(): string; + readDirectory?(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray | undefined, includes: ReadonlyArray, depth?: number): string[]; + + /* + * CompilerHost must either implement resolveModuleNames (in case if it wants to be completely in charge of + * module name resolution) or provide implementation for methods from ModuleResolutionHost (in this case compiler + * will apply built-in module resolution logic and use members of ModuleResolutionHost to ask host specific questions). + * If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just + * 'throw new Error("NotImplemented")' + */ + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference): (ResolvedModule | undefined)[]; + /** + * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files + */ + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[]; + getEnvironmentVariable?(name: string): string | undefined; + /* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void; + /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; + /* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean; + createHash?(data: string): string; +} + +/* @internal */ +export const enum TransformFlags { + None = 0, + + // Facts + // - Flags used to indicate that a node or subtree contains syntax that requires transformation. + TypeScript = 1 << 0, + ContainsTypeScript = 1 << 1, + ContainsJsx = 1 << 2, + ContainsESNext = 1 << 3, + ContainsES2017 = 1 << 4, + ContainsES2016 = 1 << 5, + ES2015 = 1 << 6, + ContainsES2015 = 1 << 7, + Generator = 1 << 8, + ContainsGenerator = 1 << 9, + DestructuringAssignment = 1 << 10, + ContainsDestructuringAssignment = 1 << 11, + + // Markers + // - Flags used to indicate that a subtree contains a specific transformation. + ContainsTypeScriptClassSyntax = 1 << 12, // Decorators, Property Initializers, Parameter Property Initializers + ContainsLexicalThis = 1 << 13, + ContainsCapturedLexicalThis = 1 << 14, + ContainsLexicalThisInComputedPropertyName = 1 << 15, + ContainsDefaultValueAssignments = 1 << 16, + ContainsRestOrSpread = 1 << 17, + ContainsObjectRestOrSpread = 1 << 18, + ContainsComputedPropertyName = 1 << 19, + ContainsBlockScopedBinding = 1 << 20, + ContainsBindingPattern = 1 << 21, + ContainsYield = 1 << 22, + ContainsHoistedDeclarationOrCompletion = 1 << 23, + ContainsDynamicImport = 1 << 24, + Super = 1 << 25, + ContainsSuper = 1 << 26, + + // Please leave this as 1 << 29. + // It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system. + // It is a good reminder of how much room we have left + HasComputedFlags = 1 << 29, // Transform flags have been computed. + + // Assertions + // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. + AssertTypeScript = TypeScript | ContainsTypeScript, + AssertJsx = ContainsJsx, + AssertESNext = ContainsESNext, + AssertES2017 = ContainsES2017, + AssertES2016 = ContainsES2016, + AssertES2015 = ES2015 | ContainsES2015, + AssertGenerator = Generator | ContainsGenerator, + AssertDestructuringAssignment = DestructuringAssignment | ContainsDestructuringAssignment, + + // Scope Exclusions + // - Bitmasks that exclude flags from propagating out of a specific context + // into the subtree flags of their container. + OuterExpressionExcludes = TypeScript | ES2015 | DestructuringAssignment | Generator | HasComputedFlags, + PropertyAccessExcludes = OuterExpressionExcludes | Super, + NodeExcludes = PropertyAccessExcludes | ContainsSuper, + ArrowFunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread, + FunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread, + ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread, + MethodOrAccessorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread, + ClassExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName, + ModuleExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion, + TypeExcludes = ~ContainsTypeScript, + ObjectLiteralExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName | ContainsObjectRestOrSpread, + ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsRestOrSpread, + VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectRestOrSpread, + ParameterExcludes = NodeExcludes, + CatchClauseExcludes = NodeExcludes | ContainsObjectRestOrSpread, + BindingPatternExcludes = NodeExcludes | ContainsRestOrSpread, + + // Masks + // - Additional bitmasks + ES2015FunctionSyntaxMask = ContainsCapturedLexicalThis | ContainsDefaultValueAssignments, +} + +export interface SourceMapRange extends TextRange { + source?: SourceMapSource; +} + +export interface SourceMapSource { + fileName: string; + text: string; + /* @internal */ lineMap: ReadonlyArray; + skipTrivia?: (pos: number) => number; +} + +/* @internal */ +export interface EmitNode { + annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup. + flags: EmitFlags; // Flags that customize emit + leadingComments?: SynthesizedComment[]; // Synthesized leading comments + trailingComments?: SynthesizedComment[]; // Synthesized trailing comments + commentRange?: TextRange; // The text range to use when emitting leading or trailing comments + sourceMapRange?: SourceMapRange; // The text range to use when emitting leading or trailing source mappings + tokenSourceMapRanges?: (SourceMapRange | undefined)[]; // The text range to use when emitting source mappings for tokens + constantValue?: string | number; // The constant value of an expression + externalHelpersModuleName?: Identifier; // The local name for an imported helpers module + helpers?: EmitHelper[]; // Emit helpers for the node + startsOnNewLine?: boolean; // If the node should begin on a new line +} + +export const enum EmitFlags { + None = 0, + SingleLine = 1 << 0, // The contents of this node should be emitted on a single line. + AdviseOnEmitNode = 1 << 1, // The printer should invoke the onEmitNode callback when printing this node. + NoSubstitution = 1 << 2, // Disables further substitution of an expression. + CapturesThis = 1 << 3, // The function captures a lexical `this` + NoLeadingSourceMap = 1 << 4, // Do not emit a leading source map location for this node. + NoTrailingSourceMap = 1 << 5, // Do not emit a trailing source map location for this node. + NoSourceMap = NoLeadingSourceMap | NoTrailingSourceMap, // Do not emit a source map location for this node. + NoNestedSourceMaps = 1 << 6, // Do not emit source map locations for children of this node. + NoTokenLeadingSourceMaps = 1 << 7, // Do not emit leading source map location for token nodes. + NoTokenTrailingSourceMaps = 1 << 8, // Do not emit trailing source map location for token nodes. + NoTokenSourceMaps = NoTokenLeadingSourceMaps | NoTokenTrailingSourceMaps, // Do not emit source map locations for tokens of this node. + NoLeadingComments = 1 << 9, // Do not emit leading comments for this node. + NoTrailingComments = 1 << 10, // Do not emit trailing comments for this node. + NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node. + NoNestedComments = 1 << 11, + HelperName = 1 << 12, + ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). + LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration. + InternalName = 1 << 15, // The name is internal to an ES5 class body function. + Indented = 1 << 16, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). + NoIndentation = 1 << 17, // Do not indent the node. + AsyncFunctionBody = 1 << 18, + ReuseTempVariableScope = 1 << 19, // Reuse the existing temp variable scope during emit. + CustomPrologue = 1 << 20, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). + NoHoisting = 1 << 21, // Do not hoist this declaration in --module system + HasEndOfDeclarationMarker = 1 << 22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration + Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable. + NoAsciiEscaping = 1 << 24, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions. + /*@internal*/ TypeScriptClassWrapper = 1 << 25, // The node is an IIFE class wrapper created by the ts transform. + /*@internal*/ NeverApplyImportHelper = 1 << 26, // Indicates the node should never be wrapped with an import star helper (because, for example, it imports tslib itself) +} + +export interface EmitHelper { + readonly name: string; // A unique name for this helper. + readonly scoped: boolean; // Indicates whether the helper MUST be emitted in the current scope. + readonly text: string | ((node: EmitHelperUniqueNameCallback) => string); // ES3-compatible raw script text, or a function yielding such a string + readonly priority?: number; // Helpers with a higher priority are emitted earlier than other helpers on the node. +} + +/* @internal */ +export type UniqueNameHandler = (baseName: string, checkFn?: (name: string) => boolean, optimistic?: boolean) => string; + +export type EmitHelperUniqueNameCallback = (name: string) => string; + +/** + * Used by the checker, this enum keeps track of external emit helpers that should be type + * checked. + */ +/* @internal */ +export const enum ExternalEmitHelpers { + Extends = 1 << 0, // __extends (used by the ES2015 class transformation) + Assign = 1 << 1, // __assign (used by Jsx and ESNext object spread transformations) + Rest = 1 << 2, // __rest (used by ESNext object rest transformation) + Decorate = 1 << 3, // __decorate (used by TypeScript decorators transformation) + Metadata = 1 << 4, // __metadata (used by TypeScript decorators transformation) + Param = 1 << 5, // __param (used by TypeScript decorators transformation) + Awaiter = 1 << 6, // __awaiter (used by ES2017 async functions transformation) + Generator = 1 << 7, // __generator (used by ES2015 generator transformation) + Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations) + Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation) + Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations) + Await = 1 << 11, // __await (used by ES2017 async generator transformation) + AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation) + AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation) + AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation) + ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation) + MakeTemplateObject = 1 << 16, // __makeTemplateObject (used for constructing template string array objects) + FirstEmitHelper = Extends, + LastEmitHelper = MakeTemplateObject, + + // Helpers included by ES2015 for..of + ForOfIncludes = Values, + + // Helpers included by ES2017 for..await..of + ForAwaitOfIncludes = AsyncValues, + + // Helpers included by ES2017 async generators + AsyncGeneratorIncludes = Await | AsyncGenerator, + + // Helpers included by yield* in ES2017 async generators + AsyncDelegatorIncludes = Await | AsyncDelegator | AsyncValues, + + // Helpers included by ES2015 spread + SpreadIncludes = Read | Spread, + +} + +export const enum EmitHint { + SourceFile, // Emitting a SourceFile + Expression, // Emitting an Expression + IdentifierName, // Emitting an IdentifierName + MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode + Unspecified, // Emitting an otherwise unspecified node +} + +/* @internal */ +export interface EmitHost extends ScriptReferenceHost, ModuleSpecifierResolutionHost { + getSourceFiles(): ReadonlyArray; + useCaseSensitiveFileNames(): boolean; + getCurrentDirectory(): string; + + /* @internal */ + isSourceFileFromExternalLibrary(file: SourceFile): boolean; + getLibFileFromReference(ref: FileReference): SourceFile | undefined; + + getCommonSourceDirectory(): string; + getCanonicalFileName(fileName: string): string; + getNewLine(): string; + + isEmitBlocked(emitFileName: string): boolean; + + getPrependNodes(): ReadonlyArray; + + writeFile: WriteFileCallback; +} + +export interface TransformationContext { + /*@internal*/ getEmitResolver(): EmitResolver; + /*@internal*/ getEmitHost(): EmitHost; + + /** Gets the compiler options supplied to the transformer. */ + getCompilerOptions(): CompilerOptions; + + /** Starts a new lexical environment. */ + startLexicalEnvironment(): void; + + /** Suspends the current lexical environment, usually after visiting a parameter list. */ + suspendLexicalEnvironment(): void; + + /** Resumes a suspended lexical environment, usually before visiting a function body. */ + resumeLexicalEnvironment(): void; + + /** Ends a lexical environment, returning any declarations. */ + endLexicalEnvironment(): Statement[] | undefined; + + /** Hoists a function declaration to the containing scope. */ + hoistFunctionDeclaration(node: FunctionDeclaration): void; + + /** Hoists a variable declaration to the containing scope. */ + hoistVariableDeclaration(node: Identifier): void; + + /** Records a request for a non-scoped emit helper in the current context. */ + requestEmitHelper(helper: EmitHelper): void; + + /** Gets and resets the requested non-scoped emit helpers. */ + readEmitHelpers(): EmitHelper[] | undefined; + + /** Enables expression substitutions in the pretty printer for the provided SyntaxKind. */ + enableSubstitution(kind: SyntaxKind): void; + + /** Determines whether expression substitutions are enabled for the provided node. */ + isSubstitutionEnabled(node: Node): boolean; + + /** + * Hook used by transformers to substitute expressions just before they + * are emitted by the pretty printer. + * + * NOTE: Transformation hooks should only be modified during `Transformer` initialization, + * before returning the `NodeTransformer` callback. + */ + onSubstituteNode: (hint: EmitHint, node: Node) => Node; + + /** + * Enables before/after emit notifications in the pretty printer for the provided + * SyntaxKind. + */ + enableEmitNotification(kind: SyntaxKind): void; + + /** + * Determines whether before/after emit notifications should be raised in the pretty + * printer when it emits a node. + */ + isEmitNotificationEnabled(node: Node): boolean; + + /** + * Hook used to allow transformers to capture state before or after + * the printer emits a node. + * + * NOTE: Transformation hooks should only be modified during `Transformer` initialization, + * before returning the `NodeTransformer` callback. + */ + onEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + + /* @internal */ addDiagnostic(diag: DiagnosticWithLocation): void; +} + +export interface TransformationResult { + /** Gets the transformed source files. */ + transformed: T[]; + + /** Gets diagnostics for the transformation. */ + diagnostics?: DiagnosticWithLocation[]; + + /** + * Gets a substitute for a node, if one is available; otherwise, returns the original node. + * + * @param hint A hint as to the intended usage of the node. + * @param node The node to substitute. + */ + substituteNode(hint: EmitHint, node: Node): Node; + + /** + * Emits a node with possible notification. + * + * @param hint A hint as to the intended usage of the node. + * @param node The node to emit. + * @param emitCallback A callback used to emit the node. + */ + emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; + + /** + * Clean up EmitNode entries on any parse-tree nodes. + */ + dispose(): void; +} + +/** + * A function that is used to initialize and return a `Transformer` callback, which in turn + * will be used to transform one or more nodes. + */ +export type TransformerFactory = (context: TransformationContext) => Transformer; + +/** + * A function that transforms a node. + */ +export type Transformer = (node: T) => T; + +/** + * A function that accepts and possibly transforms a node. + */ +export type Visitor = (node: Node) => VisitResult; + +export type VisitResult = T | T[] | undefined; + +export interface Printer { + /** + * Print a node and its subtree as-is, without any emit transformations. + * @param hint A value indicating the purpose of a node. This is primarily used to + * distinguish between an `Identifier` used in an expression position, versus an + * `Identifier` used as an `IdentifierName` as part of a declaration. For most nodes you + * should just pass `Unspecified`. + * @param node The node to print. The node and its subtree are printed as-is, without any + * emit transformations. + * @param sourceFile A source file that provides context for the node. The source text of + * the file is used to emit the original source content for literals and identifiers, while + * the identifiers of the source file are used when generating unique names to avoid + * collisions. + */ + printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; + /** + * Prints a list of nodes using the given format flags + */ + printList(format: ListFormat, list: NodeArray, sourceFile: SourceFile): string; + /** + * Prints a source file as-is, without any emit transformations. + */ + printFile(sourceFile: SourceFile): string; + /** + * Prints a bundle of source files as-is, without any emit transformations. + */ + printBundle(bundle: Bundle): string; + /*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void; + /*@internal*/ writeList(format: ListFormat, list: NodeArray | undefined, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void; + /*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void; + /*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter, info?: BundleInfo): void; +} + +/** + * When a bundle contains prepended content, we store a file on disk indicating where the non-prepended + * content of that file starts. On a subsequent build where there are no upstream .d.ts changes, we + * read the bundle info file and the original .js file to quickly re-use portion of the file + * that didn't originate in prepended content. + */ +/* @internal */ +export interface BundleInfo { + // The offset (in characters, i.e. suitable for .substr) at which the + // non-prepended portion of the emitted file starts. + originalOffset: number; + // The total length of this bundle. Used to ensure we're pulling from + // the same source as we originally wrote. + totalLength: number; +} + +export interface PrintHandlers { + /** + * A hook used by the Printer when generating unique names to avoid collisions with + * globally defined names that exist outside of the current source file. + */ + hasGlobalName?(name: string): boolean; + /** + * A hook used by the Printer to provide notifications prior to emitting a node. A + * compatible implementation **must** invoke `emitCallback` with the provided `hint` and + * `node` values. + * @param hint A hint indicating the intended purpose of the node. + * @param node The node to emit. + * @param emitCallback A callback that, when invoked, will emit the node. + * @example + * ```ts + * var printer = createPrinter(printerOptions, { + * onEmitNode(hint, node, emitCallback) { + * // set up or track state prior to emitting the node... + * emitCallback(hint, node); + * // restore state after emitting the node... + * } + * }); + * ``` + */ + onEmitNode?(hint: EmitHint, node: Node | undefined, emitCallback: (hint: EmitHint, node: Node | undefined) => void): void; + /** + * A hook used by the Printer to perform just-in-time substitution of a node. This is + * primarily used by node transformations that need to substitute one node for another, + * such as replacing `myExportedVar` with `exports.myExportedVar`. + * @param hint A hint indicating the intended purpose of the node. + * @param node The node to emit. + * @example + * ```ts + * var printer = createPrinter(printerOptions, { + * substituteNode(hint, node) { + * // perform substitution if necessary... + * return node; + * } + * }); + * ``` + */ + substituteNode?(hint: EmitHint, node: Node): Node; + /*@internal*/ onEmitSourceMapOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + /*@internal*/ onEmitSourceMapOfToken?: (node: Node | undefined, token: SyntaxKind, writer: (s: string) => void, pos: number, emitCallback: (token: SyntaxKind, writer: (s: string) => void, pos: number) => number) => number; + /*@internal*/ onEmitSourceMapOfPosition?: (pos: number) => void; + /*@internal*/ onSetSourceFile?: (node: SourceFile) => void; + /*@internal*/ onBeforeEmitNodeArray?: (nodes: NodeArray | undefined) => void; + /*@internal*/ onAfterEmitNodeArray?: (nodes: NodeArray | undefined) => void; + /*@internal*/ onBeforeEmitToken?: (node: Node) => void; + /*@internal*/ onAfterEmitToken?: (node: Node) => void; +} + +export interface PrinterOptions { + removeComments?: boolean; + newLine?: NewLineKind; + omitTrailingSemicolon?: boolean; + noEmitHelpers?: boolean; + /*@internal*/ module?: CompilerOptions["module"]; + /*@internal*/ target?: CompilerOptions["target"]; + /*@internal*/ sourceMap?: boolean; + /*@internal*/ inlineSourceMap?: boolean; + /*@internal*/ extendedDiagnostics?: boolean; + /*@internal*/ onlyPrintJsDocStyle?: boolean; + /*@internal*/ neverAsciiEscape?: boolean; +} + +/* @internal */ +export interface EmitTextWriter extends SymbolWriter { + write(s: string): void; + writeTextOfNode(text: string, node: Node): void; + getText(): string; + rawWrite(s: string): void; + writeLiteral(s: string): void; + getTextPos(): number; + getLine(): number; + getColumn(): number; + getIndent(): number; + isAtStartOfLine(): boolean; +} + +export interface GetEffectiveTypeRootsHost { + directoryExists?(directoryName: string): boolean; + getCurrentDirectory?(): string; +} +/** @internal */ +export interface ModuleSpecifierResolutionHost extends GetEffectiveTypeRootsHost { + useCaseSensitiveFileNames?(): boolean; + fileExists?(path: string): boolean; + readFile?(path: string): string | undefined; +} + +// Note: this used to be deprecated in our public API, but is still used internally +/* @internal */ +export interface SymbolTracker { + // Called when the symbol writer encounters a symbol to write. Currently only used by the + // declaration emitter to help determine if it should patch up the final declaration file + // with import statements it previously saw (but chose not to emit). + trackSymbol?(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; + reportInaccessibleThisError?(): void; + reportPrivateInBaseOfClassExpression?(propertyName: string): void; + reportInaccessibleUniqueSymbolError?(): void; + moduleResolverHost?: ModuleSpecifierResolutionHost & { getSourceFiles(): ReadonlyArray, getCommonSourceDirectory(): string }; + trackReferencedAmbientModule?(decl: ModuleDeclaration, symbol: Symbol): void; + trackExternalModuleSymbolOfImportTypeNode?(symbol: Symbol): void; +} + +export interface TextSpan { + start: number; + length: number; +} + +export interface TextChangeRange { + span: TextSpan; + newLength: number; +} + +/* @internal */ +export interface DiagnosticCollection { + // Adds a diagnostic to this diagnostic collection. + add(diagnostic: Diagnostic): void; + + // Returns the first existing diagnostic that is equivalent to the given one (sans related information) + lookup(diagnostic: Diagnostic): Diagnostic | undefined; + + // Gets all the diagnostics that aren't associated with a file. + getGlobalDiagnostics(): Diagnostic[]; + + // If fileName is provided, gets all the diagnostics associated with that file name. + // Otherwise, returns all the diagnostics (global and file associated) in this collection. + getDiagnostics(fileName: string): DiagnosticWithLocation[]; + getDiagnostics(): Diagnostic[]; + + reattachFileDiagnostics(newFile: SourceFile): void; +} + +// SyntaxKind.SyntaxList +export interface SyntaxList extends Node { + _children: Node[]; +} + +export const enum ListFormat { + None = 0, + + // Line separators + SingleLine = 0, // Prints the list on a single line (default). + MultiLine = 1 << 0, // Prints the list on multiple lines. + PreserveLines = 1 << 1, // Prints the list using line preservation if possible. + LinesMask = SingleLine | MultiLine | PreserveLines, + + // Delimiters + NotDelimited = 0, // There is no delimiter between list items (default). + BarDelimited = 1 << 2, // Each list item is space-and-bar (" |") delimited. + AmpersandDelimited = 1 << 3, // Each list item is space-and-ampersand (" &") delimited. + CommaDelimited = 1 << 4, // Each list item is comma (",") delimited. + DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited, + + AllowTrailingComma = 1 << 5, // Write a trailing comma (",") if present. + + // Whitespace + Indented = 1 << 6, // The list should be indented. + SpaceBetweenBraces = 1 << 7, // Inserts a space after the opening brace and before the closing brace. + SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node. + + // Brackets/Braces + Braces = 1 << 9, // The list is surrounded by "{" and "}". + Parenthesis = 1 << 10, // The list is surrounded by "(" and ")". + AngleBrackets = 1 << 11, // The list is surrounded by "<" and ">". + SquareBrackets = 1 << 12, // The list is surrounded by "[" and "]". + BracketsMask = Braces | Parenthesis | AngleBrackets | SquareBrackets, + + OptionalIfUndefined = 1 << 13, // Do not emit brackets if the list is undefined. + OptionalIfEmpty = 1 << 14, // Do not emit brackets if the list is empty. + Optional = OptionalIfUndefined | OptionalIfEmpty, + + // Other + PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes. + NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list. + NoInterveningComments = 1 << 17, // Do not emit comments between each node + + NoSpaceIfEmpty = 1 << 18, // If the literal is empty, do not add spaces between braces. + SingleElement = 1 << 19, + + // Precomputed Formats + Modifiers = SingleLine | SpaceBetweenSiblings | NoInterveningComments, + HeritageClauses = SingleLine | SpaceBetweenSiblings, + SingleLineTypeLiteralMembers = SingleLine | SpaceBetweenBraces | SpaceBetweenSiblings, + MultiLineTypeLiteralMembers = MultiLine | Indented | OptionalIfEmpty, + + TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine, + UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, + IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, + ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings | NoSpaceIfEmpty, + ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings | NoSpaceIfEmpty, + ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces | NoSpaceIfEmpty, + ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets, + CommaListElements = CommaDelimited | SpaceBetweenSiblings | SingleLine, + CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis, + NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined, + TemplateExpressionSpans = SingleLine | NoInterveningComments, + SingleLineBlockStatements = SpaceBetweenBraces | SpaceBetweenSiblings | SingleLine, + MultiLineBlockStatements = Indented | MultiLine, + VariableDeclarationList = CommaDelimited | SpaceBetweenSiblings | SingleLine, + SingleLineFunctionBodyStatements = SingleLine | SpaceBetweenSiblings | SpaceBetweenBraces, + MultiLineFunctionBodyStatements = MultiLine, + ClassHeritageClauses = SingleLine, + ClassMembers = Indented | MultiLine, + InterfaceMembers = Indented | MultiLine, + EnumMembers = CommaDelimited | Indented | MultiLine, + CaseBlockClauses = Indented | MultiLine, + NamedImportsOrExportsElements = CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | SingleLine | SpaceBetweenBraces | NoSpaceIfEmpty, + JsxElementOrFragmentChildren = SingleLine | NoInterveningComments, + JsxElementAttributes = SingleLine | SpaceBetweenSiblings | NoInterveningComments, + CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty, + HeritageClauseTypes = CommaDelimited | SpaceBetweenSiblings | SingleLine, + SourceFileStatements = MultiLine | NoTrailingNewLine, + Decorators = MultiLine | Optional, + TypeArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | AngleBrackets | Optional, + TypeParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | AngleBrackets | Optional, + Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis, + IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets, +} + +/* @internal */ +export const enum PragmaKindFlags { + None = 0, + /** + * Triple slash comment of the form + * /// + */ + TripleSlashXML = 1 << 0, + /** + * Single line comment of the form + * // @pragma-name argval1 argval2 + * or + * /// @pragma-name argval1 argval2 + */ + SingleLine = 1 << 1, + /** + * Multiline non-jsdoc pragma of the form + * /* @pragma-name argval1 argval2 * / + */ + MultiLine = 1 << 2, + All = TripleSlashXML | SingleLine | MultiLine, + Default = All, +} + +/* @internal */ +interface PragmaArgumentSpecification { + name: TName; // Determines the name of the key in the resulting parsed type, type parameter to cause literal type inference + optional?: boolean; + captureSpan?: boolean; +} + +/* @internal */ +export interface PragmaDefinition { + args?: + | [PragmaArgumentSpecification] + | [PragmaArgumentSpecification, PragmaArgumentSpecification] + | [PragmaArgumentSpecification, PragmaArgumentSpecification, PragmaArgumentSpecification] + | [PragmaArgumentSpecification, PragmaArgumentSpecification, PragmaArgumentSpecification, PragmaArgumentSpecification]; + // If not present, defaults to PragmaKindFlags.Default + kind?: PragmaKindFlags; +} + +/** + * This function only exists to cause exact types to be inferred for all the literals within `commentPragmas` + */ +/* @internal */ +function _contextuallyTypePragmas}, K1 extends string, K2 extends string, K3 extends string, K4 extends string>(args: T): T { + return args; +} + +// While not strictly a type, this is here because `PragmaMap` needs to be here to be used with `SourceFile`, and we don't +// fancy effectively defining it twice, once in value-space and once in type-space +/* @internal */ +export const commentPragmas = _contextuallyTypePragmas({ + "reference": { + args: [ + { name: "types", optional: true, captureSpan: true }, + { name: "lib", optional: true, captureSpan: true }, + { name: "path", optional: true, captureSpan: true }, + { name: "no-default-lib", optional: true } + ], + kind: PragmaKindFlags.TripleSlashXML + }, + "amd-dependency": { + args: [{ name: "path" }, { name: "name", optional: true }], + kind: PragmaKindFlags.TripleSlashXML + }, + "amd-module": { + args: [{ name: "name" }], + kind: PragmaKindFlags.TripleSlashXML + }, + "ts-check": { + kind: PragmaKindFlags.SingleLine + }, + "ts-nocheck": { + kind: PragmaKindFlags.SingleLine + }, + "jsx": { + args: [{ name: "factory" }], + kind: PragmaKindFlags.MultiLine + }, +}); + +/* @internal */ +type PragmaArgTypeMaybeCapture = TDesc extends {captureSpan: true} ? {value: string, pos: number, end: number} : string; + +/* @internal */ +type PragmaArgTypeOptional = + TDesc extends {optional: true} + ? {[K in TName]?: PragmaArgTypeMaybeCapture} + : {[K in TName]: PragmaArgTypeMaybeCapture}; + +/** + * Maps a pragma definition into the desired shape for its arguments object + * Maybe the below is a good argument for types being iterable on struture in some way. + */ +/* @internal */ +type PragmaArgumentType = + T extends { args: [PragmaArgumentSpecification, PragmaArgumentSpecification, PragmaArgumentSpecification, PragmaArgumentSpecification] } + ? PragmaArgTypeOptional & PragmaArgTypeOptional & PragmaArgTypeOptional & PragmaArgTypeOptional + : T extends { args: [PragmaArgumentSpecification, PragmaArgumentSpecification, PragmaArgumentSpecification] } + ? PragmaArgTypeOptional & PragmaArgTypeOptional & PragmaArgTypeOptional + : T extends { args: [PragmaArgumentSpecification, PragmaArgumentSpecification] } + ? PragmaArgTypeOptional & PragmaArgTypeOptional + : T extends { args: [PragmaArgumentSpecification] } + ? PragmaArgTypeOptional + : object; +// The above fallback to `object` when there's no args to allow `{}` (as intended), but not the number 2, for example +// TODO: Swap to `undefined` for a cleaner API once strictNullChecks is enabled + +type ConcretePragmaSpecs = typeof commentPragmas; + +/* @internal */ +export type PragmaPseudoMap = {[K in keyof ConcretePragmaSpecs]?: {arguments: PragmaArgumentType, range: CommentRange}}; + +/* @internal */ +export type PragmaPseudoMapEntry = {[K in keyof PragmaPseudoMap]: {name: K, args: PragmaPseudoMap[K]}}[keyof PragmaPseudoMap]; + +/* @internal */ +export interface ReadonlyPragmaMap extends ReadonlyMap { + get(key: TKey): PragmaPseudoMap[TKey] | PragmaPseudoMap[TKey][]; + forEach(action: (value: PragmaPseudoMap[TKey] | PragmaPseudoMap[TKey][], key: TKey) => void): void; +} + +/** + * A strongly-typed es6 map of pragma entries, the values of which are either a single argument + * value (if only one was found), or an array of multiple argument values if the pragma is present + * in multiple places + */ +/* @internal */ +export interface PragmaMap extends Map, ReadonlyPragmaMap { + set(key: TKey, value: PragmaPseudoMap[TKey] | PragmaPseudoMap[TKey][]): this; + get(key: TKey): PragmaPseudoMap[TKey] | PragmaPseudoMap[TKey][]; + forEach(action: (value: PragmaPseudoMap[TKey] | PragmaPseudoMap[TKey][], key: TKey) => void): void; +} + +export interface UserPreferences { + readonly disableSuggestions?: boolean; + readonly quotePreference?: "double" | "single"; + readonly includeCompletionsForModuleExports?: boolean; + readonly includeCompletionsWithInsertText?: boolean; + readonly importModuleSpecifierPreference?: "relative" | "non-relative"; + /** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */ + readonly importModuleSpecifierEnding?: "minimal" | "index" | "js"; + readonly allowTextChangesInNewFiles?: boolean; +} diff --git a/src/utilities.ts b/src/utilities.ts new file mode 100644 index 0000000..0b12275 --- /dev/null +++ b/src/utilities.ts @@ -0,0 +1,148 @@ +/** + * @file 工具函数 + * @author meixuguang + */ + +import { + EmitTextWriter +} from './types' +import { + computeLineStarts +} from './scanner'; +import { + last, + noop, + map +} from './core' +import {Node} from 'typescript'; +import * as ts from 'typescript'; + +const indentStrings: string[] = ["", " "]; +export function getIndentString(level: number) { + if (indentStrings[level] === undefined) { + indentStrings[level] = getIndentString(level - 1) + indentStrings[1]; + } + return indentStrings[level]; +} + +export function getIndentSize() { + return indentStrings[1].length; +} + +export function createTextWriter(newLine: string): EmitTextWriter { + let output: string; + let indent: number; + let lineStart: boolean; + let lineCount: number; + let linePos: number; + + function updateLineCountAndPosFor(s: string) { + const lineStartsOfS = computeLineStarts(s); + if (lineStartsOfS.length > 1) { + lineCount = lineCount + lineStartsOfS.length - 1; + linePos = output.length - s.length + last(lineStartsOfS); + lineStart = (linePos - output.length) === 0; + } + else { + lineStart = false; + } + } + + function write(s: string) { + if (s && s.length) { + if (lineStart) { + s = getIndentString(indent) + s; + lineStart = false; + } + output += s; + updateLineCountAndPosFor(s); + } + } + + function reset(): void { + output = ""; + indent = 0; + lineStart = true; + lineCount = 0; + linePos = 0; + } + + function rawWrite(s: string) { + if (s !== undefined) { + output += s; + updateLineCountAndPosFor(s); + } + } + + function writeLiteral(s: string) { + if (s && s.length) { + write(s); + } + } + + function writeLine() { + if (!lineStart) { + output += newLine; + lineCount++; + linePos = output.length; + lineStart = true; + } + } + + function writeTextOfNode(text: string, node: Node) { + + } + + reset(); + + return { + write, + rawWrite, + writeTextOfNode, + writeLiteral, + writeLine, + increaseIndent: () => { indent++; }, + decreaseIndent: () => { indent--; }, + getIndent: () => indent, + getTextPos: () => output.length, + getLine: () => lineCount, + getColumn: () => lineStart ? indent * getIndentSize() : output.length - linePos, + getText: () => output, + isAtStartOfLine: () => lineStart, + clear: reset, + reportInaccessibleThisError: noop, + reportPrivateInBaseOfClassExpression: noop, + reportInaccessibleUniqueSymbolError: noop, + trackSymbol: noop, + writeKeyword: write, + writeOperator: write, + writeParameter: write, + writeProperty: write, + writePunctuation: write, + writeSpace: write, + writeStringLiteral: write, + writeSymbol: write + }; +} + + +export function showSymbol(symbol: ts.Symbol): string { + const symbolFlags = ts.SymbolFlags; + return `{ flags: ${symbolFlags ? showFlags(symbol.flags, symbolFlags) : symbol.flags}; declarations: ${map(symbol.declarations, showSyntaxKind)} }`; +} + +function showFlags(flags: number, flagsEnum: { [flag: number]: string }): string { + const out: string[] = []; + for (let pow = 0; pow <= 30; pow++) { + const n = 1 << pow; + if (flags & n) { + out.push(flagsEnum[n]); + } + } + return out.join("|"); +} + +export function showSyntaxKind(node: Node): string { + const syntaxKind = ts.SyntaxKind; + return syntaxKind ? syntaxKind[node.kind] : node.kind.toString(); +} \ No newline at end of file