1
+ /// <reference path="..\harness.ts" />
2
+ /// <reference path="..\..\harness\harnessLanguageService.ts" />
3
+
4
+ namespace ts {
5
+ describe ( "Type Checker Public Relationship APIs" , ( ) => {
6
+ let checker : TypeChecker ;
7
+ let host : CompilerHost ;
8
+ let program : Program ;
9
+ before ( ( ) => {
10
+ host = Harness . Compiler . createCompilerHost ( [ {
11
+ unitName : "test.ts" ,
12
+ content : `
13
+ type FunctionAlias = Function;
14
+ function foo() {
15
+ type Function = { myBrand: 42 } & FunctionAlias;
16
+ return (() => {}) as any as Function;
17
+ }
18
+ function foo2<T>(x: T) {
19
+ type Function = { myBrand: T } & FunctionAlias;
20
+ const ret = (() => {}) as any as Function;
21
+ ret.myBrand = x;
22
+ return ret;
23
+ }`
24
+ } ] , ( ) => void 0 , ScriptTarget . ES3 , /*useCaseSensitiveFileNames*/ true , "" , NewLineKind . CarriageReturnLineFeed ) ;
25
+ program = ts . createProgram ( [ "test.ts" ] , ts . defaultInitCompilerOptions , host ) ;
26
+ const diag = ts . getPreEmitDiagnostics ( program ) ;
27
+ if ( diag . length ) {
28
+ const errors = ts . formatDiagnostics ( diag , host ) ;
29
+ console . log ( errors ) ;
30
+ }
31
+ checker = program . getTypeChecker ( ) ;
32
+ } ) ;
33
+
34
+ it ( "can get the any type" , ( ) => {
35
+ assert ( checker . getAnyType ( ) . flags & TypeFlags . Any ) ;
36
+ } ) ;
37
+
38
+ it ( "can get the string type" , ( ) => {
39
+ assert ( checker . getStringType ( ) . flags & TypeFlags . String ) ;
40
+ } ) ;
41
+
42
+ it ( "can get the number type" , ( ) => {
43
+ assert ( checker . getNumberType ( ) . flags & TypeFlags . Number ) ;
44
+ } ) ;
45
+
46
+ it ( "can get the boolean type" , ( ) => {
47
+ assert ( checker . getBooleanType ( ) . flags & TypeFlags . Boolean ) ;
48
+ } ) ;
49
+
50
+ it ( "can get the void type" , ( ) => {
51
+ assert ( checker . getVoidType ( ) . flags & TypeFlags . Void ) ;
52
+ } ) ;
53
+
54
+ it ( "can get the undefined type" , ( ) => {
55
+ assert ( checker . getUndefinedType ( ) . flags & TypeFlags . Undefined ) ;
56
+ } ) ;
57
+
58
+ it ( "can get the null type" , ( ) => {
59
+ assert ( checker . getNullType ( ) . flags & TypeFlags . Null ) ;
60
+ } ) ;
61
+
62
+ it ( "can get the essymbol type" , ( ) => {
63
+ assert ( checker . getESSymbolType ( ) . flags & TypeFlags . ESSymbol ) ;
64
+ } ) ;
65
+
66
+ it ( "can get the never type" , ( ) => {
67
+ assert ( checker . getNeverType ( ) . flags & TypeFlags . Never ) ;
68
+ } ) ;
69
+
70
+ it ( "can get the unknown type" , ( ) => {
71
+ assert ( checker . getUnknownType ( ) . flags & TypeFlags . Any ) ;
72
+ assert ( checker . getUnknownType ( ) === checker . getUnknownType ( ) ) ;
73
+ } ) ;
74
+
75
+ it ( "can get the true type" , ( ) => {
76
+ assert ( checker . getTrueType ( ) . flags & TypeFlags . BooleanLiteral ) ;
77
+ } ) ;
78
+
79
+ it ( "can get the false type" , ( ) => {
80
+ assert ( checker . getFalseType ( ) . flags & TypeFlags . BooleanLiteral ) ;
81
+ } ) ;
82
+
83
+ it ( "ensures true and false are different types" , ( ) => {
84
+ assert ( checker . getFalseType ( ) !== checker . getTrueType ( ) ) ;
85
+ } ) ;
86
+
87
+ it ( "can get string literal types" , ( ) => {
88
+ assert ( ( checker . getStringLiteralType ( "foobar" ) as LiteralType ) . text === "foobar" ) ;
89
+ } ) ;
90
+
91
+ it ( "can get numeber literal types" , ( ) => {
92
+ assert ( ( checker . getNumberLiteralType ( "42" ) as LiteralType ) . text === "42" ) ;
93
+ } ) ;
94
+
95
+ it ( "doesn't choke on exceptional input to literal type getters" , ( ) => {
96
+ assert . equal ( ( checker . getStringLiteralType ( "" ) as LiteralType ) . text , "" ) ;
97
+ assert . throws ( ( ) => checker . getStringLiteralType ( undefined ) , Error , "Debug Failure. False expression:" ) ;
98
+ /* tslint:disable:no-null-keyword */
99
+ assert . throws ( ( ) => checker . getStringLiteralType ( null ) , Error , "Debug Failure. False expression:" ) ;
100
+ /* tslint:enable:no-null-keyword */
101
+ let hugeStringLiteral = map ( new Array ( 2 ** 16 - 1 ) , ( ) => "a" ) . join ( ) ;
102
+ assert . equal ( ( checker . getStringLiteralType ( hugeStringLiteral ) as LiteralType ) . text , hugeStringLiteral ) ;
103
+ hugeStringLiteral = undefined ;
104
+
105
+
106
+ assert . throws ( ( ) => checker . getNumberLiteralType ( undefined ) , Error , "Debug Failure. False expression:" ) ;
107
+ /* tslint:disable:no-null-keyword */
108
+ assert . throws ( ( ) => checker . getNumberLiteralType ( null ) , Error , "Debug Failure. False expression:" ) ;
109
+ /* tslint:enable:no-null-keyword */
110
+
111
+ const sanityChecks = [ "000" , "0b0" , "0x0" , "0.0" , "0e-0" , "-010" , "-0b10" , "-0x10" , "-0o10" , "-10.0" , "-1e-1" , "NaN" , "Infinity" , "-Infinity" ] ;
112
+ forEach ( sanityChecks , num => {
113
+ assert . equal ( ( checker . getNumberLiteralType ( num ) as LiteralType ) . text , num , `${ num } did not match.` ) ;
114
+ } ) ;
115
+
116
+ const insanityChecks = [ [ 0 , "0" ] , [ 0b0 , "0" ] , [ - 10 , "-10" ] , [ NaN , "NaN" ] , [ Infinity , "Infinity" ] , [ - Infinity , "-Infinity" ] ] ;
117
+ forEach ( insanityChecks , ( [ num , expected ] ) => {
118
+ assert . equal ( ( checker . getNumberLiteralType ( num as any ) as LiteralType ) . text , expected , `${ JSON . stringify ( num ) } should be ${ expected } ` ) ;
119
+ } ) ;
120
+
121
+ const instabilityChecks = [ { foo : 42 } , new Date ( 42 ) , [ 42 ] , new Number ( 42 ) , new String ( "42" ) ] ;
122
+ forEach ( instabilityChecks , ( bad ) => {
123
+ assert . throws ( ( ) => checker . getNumberLiteralType ( bad as any ) ) ;
124
+ } ) ;
125
+ } ) ;
126
+
127
+ it ( "can look up global types" , ( ) => {
128
+ assert . equal ( checker . lookupGlobalType ( "Array" ) . symbol . name , "Array" , "Array global symbol not named Array" ) ;
129
+ const globalFunction = checker . lookupGlobalType ( "Function" ) ;
130
+ const globalAlias = checker . lookupGlobalType ( "FunctionAlias" ) ;
131
+ assert . notEqual ( globalFunction , checker . getUnknownType ( ) , "The global function type should not be the unknown type" ) ;
132
+ assert . notEqual ( globalAlias , checker . getUnknownType ( ) , "The global alias function type should not be the unknown type" ) ;
133
+ const globalFunctionLength = globalFunction . getProperty ( "length" ) ;
134
+ const aliasFunctionLength = globalAlias . getProperty ( "length" ) ;
135
+ assert ( globalFunctionLength , "Global function length symbol should exist" ) ;
136
+ assert ( aliasFunctionLength , "Alias function length symbol should exist" ) ;
137
+ assert . notEqual ( checker . getTypeOfSymbol ( globalFunctionLength ) , checker . getUnknownType ( ) , "The global function's length type should not be unknown" ) ;
138
+ assert . notEqual ( checker . getTypeOfSymbol ( aliasFunctionLength ) , checker . getUnknownType ( ) , "The alias function's length type should not be unknown" ) ;
139
+ assert . equal ( checker . getTypeOfSymbol ( globalFunctionLength ) , checker . getTypeOfSymbol ( aliasFunctionLength ) , "Alias and global function length were not identical types" ) ;
140
+ assert . equal ( ( checker . getTypeOfSymbol ( globalFunctionLength ) as IntrinsicType ) . intrinsicName , ( checker . getNumberType ( ) as IntrinsicType ) . intrinsicName , "Function length was not number type" ) ;
141
+ } ) ;
142
+
143
+ it ( "can look up types in a given scope" , ( ) => {
144
+ assert ( program . getSourceFile ( "test.ts" ) , "Test file not found" ) ;
145
+ const functionBody = forEachChild ( program . getSourceFile ( "test.ts" ) , node => node . kind === SyntaxKind . FunctionDeclaration ? ( node as FunctionDeclaration ) : undefined ) . body ;
146
+ assert ( functionBody , "Function body missing" ) ;
147
+ const innerFunction = checker . lookupTypeAt ( "Function" , functionBody . statements [ functionBody . statements . length - 1 ] ) ;
148
+ assert ( innerFunction , "Inner function type missing" ) ;
149
+ assert . notEqual ( innerFunction , checker . getUnknownType ( ) , "Inner function type should not be unknown" ) ;
150
+ assert . notEqual ( checker . lookupGlobalType ( "Function" ) , innerFunction , "Inner function type should be different than global" ) ;
151
+ const brandNameType = checker . getTypeOfSymbol ( innerFunction . getProperty ( "myBrand" ) ) ;
152
+ assert . notEqual ( brandNameType , checker . getUnknownType ( ) , "Brand type on inner function should not be unknown" ) ;
153
+ assert . equal ( brandNameType , checker . getNumberLiteralType ( "42" ) , "Brand type should be 42" ) ;
154
+
155
+ let skipped = false ;
156
+ const functionBody2 = forEachChild ( program . getSourceFile ( "test.ts" ) , node => node . kind === SyntaxKind . FunctionDeclaration ? skipped ? ( node as FunctionDeclaration ) : ( skipped = true , undefined ) : undefined ) . body ;
157
+ assert ( functionBody2 , "Function body missing" ) ;
158
+ const innerFunction2 = checker . lookupTypeAt ( "Function" , functionBody2 . statements [ functionBody2 . statements . length - 1 ] ) ;
159
+ assert ( innerFunction2 , "Inner function type missing" ) ;
160
+ assert . notEqual ( innerFunction2 , checker . getUnknownType ( ) , "Inner function type should not be unknown" ) ;
161
+ assert . notEqual ( checker . lookupGlobalType ( "Function" ) , innerFunction2 , "Inner function type should be different than global" ) ;
162
+ const brandNameType2 = checker . getTypeOfSymbol ( innerFunction2 . getProperty ( "myBrand" ) ) ;
163
+ assert . notEqual ( brandNameType2 , checker . getUnknownType ( ) , "Brand type on inner function should not be unknown" ) ;
164
+ const functionType = checker . lookupGlobalValueType ( "foo2" ) ;
165
+ assert . notEqual ( functionType , checker . getUnknownType ( ) , "foo2 function type should not be unknown" ) ;
166
+ assert ( brandNameType2 . flags & TypeFlags . TypeParameter , "Brand should be a type parameter" ) ;
167
+ assert . equal ( brandNameType2 , checker . lookupGlobalValueType ( "foo2" ) . getCallSignatures ( ) [ 0 ] . getTypeParameters ( ) [ 0 ] , "Brand type should be a type parameter" ) ;
168
+ } ) ;
169
+
170
+ it ( "can compare types using all the builtin relationships" , ( ) => {
171
+ assert ( checker . isSubtypeOf ( checker . getNumberType ( ) , checker . getAnyType ( ) ) , "Any should be a subtype of number" ) ;
172
+ assert . isFalse ( checker . isSubtypeOf ( checker . getAnyType ( ) , checker . getNumberType ( ) ) , "Number should not be a subtype of any" ) ;
173
+
174
+ assert ( checker . isAssignableTo ( checker . getAnyType ( ) , checker . getNumberType ( ) ) , "Any should be assignable to number" ) ;
175
+ assert ( checker . isAssignableTo ( checker . getFalseType ( ) , checker . getBooleanType ( ) ) , "False should be assignable to boolean" ) ;
176
+
177
+ assert ( checker . isComparableTo ( checker . getBooleanType ( ) , checker . getFalseType ( ) ) , "False and boolean are comparable" ) ;
178
+ assert ( checker . isComparableTo ( checker . getFalseType ( ) , checker . getBooleanType ( ) ) , "Boolean and false are comparable" ) ;
179
+ } ) ;
180
+
181
+ after ( ( ) => {
182
+ checker = undefined ;
183
+ host = undefined ;
184
+ program = undefined ;
185
+ } ) ;
186
+ } ) ;
187
+ }
0 commit comments