-
Notifications
You must be signed in to change notification settings - Fork 0
- Common Feature Requests
-
Type System Behavior
- What is structural typing?
- What is type erasure?
- Why are getters without setters not considered read-only?
- Why are function parameters bivariant?
- Why are functions with fewer parameters assignable to functions that take more parameters?
- Why are functions returning non-
void
assignable to function returningvoid
? - Why are all types assignable to empty interfaces?
- Can I make a type alias nominal?
- How do I prevent two types from being structurally compatible?
- How do I check at runtime if an object implements some interface?
- Why doesn't this incorrect cast throw a runtime error?
- Why don't I get type checking for
(number) => string
or(T) => T
? - Why am I getting an error about a missing index signature?
- Functions
-
Classes
- Why do these empty classes behave strangely?
- When and why are classes nominal?
- Why does
this
get orphaned in my instance methods? - What's the difference between
Bar
andtypeof Bar
whenBar
is aclass
? - Why do my derived class property initializers overwrite values set in the base class constructor?
- What's the difference between
declare class
andinteface
? - What does it mean for an interface to extend a class?
- Why am I getting "TypeError: [base class name] is not defined in
__extends
? - Why am I getting "TypeError: Cannot read property 'prototype' of undefined" in
__extends
?
- Generics
- Modules
- Enums
- Type Guards
- Decorators
- JSX and React
- Things That Don't Work
- External Tools
- Commandline Behavior
- Glossary and Terms in this FAQ
I want to request one of the following features...
Here's a list of common feature requests and their corresponding issue. Please leave comments in these rather than logging new issues.
- Non-nullable values #185
- Minification #8
- Read-only properties #12
- Extension methods #9
- Partial classes #563
- Non-numeric or string-based
enum
s #1206 - Wildcards or globbing in tsconfig.json: #1927
- Safe navigation operator, AKA CoffeeScript's null conditional/propagating/propagation operator, AKA C#'s' ?. operator #16
- Something to do with
this
#513 - Generic type parameter defaults #2175
- Strong typing of
Function
memberscall
/bind
/apply
#212 - Function overloading #3442
- Referencing generic components in JSX #6395
TypeScript uses structural typing. This system is different than the type system employed by some other popular languages you may have used (e.g. Java, C#, etc.)
The idea behind structural typing is that two types are compatible if their members are compatible.
For example, in C# or Java, two classes named MyPoint
and YourPoint
, both with public int
properties x
and y
, are not interchangeable, even though they are identical.
But in a structural type system, the fact that these types have different names is immaterial.
Because they have the same members with the same types, they are identical.
This applies to subtype relationships as well.
In C++, for example, you could only use a Dog
in place of an Animal
if Animal
was explicitly in Dog
's class heritage.
In TypeScript, this is not the case -- a Dog
with at least as many members (with appropriate types) as Animal
is a subtype of Animal
regardless of explicit heritage.
This can have some surprising consequences for programmers accustomed to working in a nominally-typed language. Many questions in this FAQ trace their roots to structural typing and its implications. Once you grasp the basics of it, however, it's very easy to reason about.
TypeScript removes type annotations, interfaces, type aliases, and other type system constructs during compilation.
Input:
var x: SomeInterface;
Output:
var x;
This means that at run-time, there is no information present that says that some variable x
was declared as being of type SomeInterface
.
The lack of run-time type information can be surprising for programmers who are used to extensively using reflection or other metadata systems. Many questions in this FAQ boil down to "because types are erased".
I wrote some code like this and expected an error:
class Foo { get bar() { return 42; } } let x = new Foo(); // Expected error here x.bar = 10;
A getter without a setter does not create a read-only property. See #12 for the suggestion tracking this issue.
I wrote some code like this and expected an error:
function trainDog(d: Dog) { ... } function cloneAnimal(source: Animal, done: (result: Animal) => void): void { ... } let c = new Cat(); // Runtime error here occurs because we end up invoking 'trainDog' with a 'Cat' cloneAnimal(c, trainDog);
This is a unsoundness resulting from the lack of explicit covariant / contravariant annotations in the type system.
To see why this happens, consider two questions: Is Dog[]
a subtype of Animal[]
? Should Dog[]
be a subtype of Animal[]
?
The second question (should Dog[]
be a subtype of Animal[]
?) is an easier one.
What if the answer was "no" ?
function checkAnimalsAreAwake(arr: Animal[]) { ... }
let myPets: Dog[] = [spot, fido];
// Error? Can't substitute Dog[] for Animal[] ?
checkAnimalsAreAwake(myPets);
This would be incredibly annoying.
The code here is 100% correct.
There's not a good reason to reject this program on the basis that Dog[]
can't be used in place of Animal[]
-- clearly a group of Dog
s is a group of Animal
s.
Back to the first question.
When the type system is deciding whether or not Dog[]
is a subtype of Animal[]
, it does the following computation (written here as if the compiler took no optimizations), among many others:
- Is
Dog[]
assignable to Animal[]` ? - Is each member of
Dog[]
assignable toAnimal[]
?- Is
Dog[].push
assignable toAnimal[].push
?- Is the type
(x: Dog) => number
assignable to(x: Animal) => number
?- Is the first parameter type in
(x: Dog) => number
assignable to or from first parameter type in(x: Animal) => number
?- Is
Dog
assignable to or fromAnimal
?- Yes
- Is
- Is the first parameter type in
- Is the type
- Is
As you can see here, the relevant question Is the type (x: Dog) => number
assignable to (x: Animal) => number
? is the same question
being asked when we were passing a possibly-too-specific callback to a function.
In summary, in the TypeScript type system, the question of whether a more-specific-type-accepting function should be assignable to a function accpting a less-specific type provides a prerequisite answer to whether an array of that more specific type should be assignable to an array of a less specific type. Having the latter not be the case would not be an acceptable type system in the vast majority of cases, so we have to take a correctness trade-off for the specific case of function argument types.
I wrote some code like this and expected an error:
function handler(arg: string) { // .... } function doSomething(callback: (arg1: string, arg2: number) => void) { callback('hello', 42); } // Expected error because 'doSomething' wants a callback of // 2 parameters, but 'handler' only accepts 1 doSomething(handler);
This is the expected and desired behavior.
First, refer to the "substitutability" primer at the top of the FAQ -- handler
is a valid argument for callback
because it can safely ignored extra parameters.
Second, let's consider another case:
let items = [1, 2, 3];
items.forEach(arg => console.log(arg));
This is isomorphic to the example that "wanted" an error.
At runtime, forEach
invokes the given callback with three arguments (value, index, array), but most of the time the callback only uses one or two of the arguments.
This is a very common JavaScript pattern and it would be burdensome to have to explicitly declare unused parameters.
I wrote some code like this and expected an error:
function doSomething(): number { return 42; } function callMeMaybe(callback: () => void) { callback(); } // Expected an error because 'doSomething' returns number, but 'callMeMaybe' expects void-returning function callMeMaybe(doSomething);
This is the expected and desired behavior.
First, refer to the "substitutability" primer -- the fact that doSomething
returns "more" information than callMeMaybe
is a valid substitution.
Second, let's consider another case:
let items = [1, 2];
callMeMaybe(() => items.push(3));
This is isomorphic to the example that "wanted" an error.
Array#push
returns a number (the new length of the array), but it's a safe substitute to use for a void
-returning function.
Another way to think of this is that a void
-returning callback type says "I'm not going to look at your return value, if one exists".
I wrote some code like this an expected an error:
interface Thing { /* nothing here */ } function doSomething(a: Thing) { // mysterious implementation here } // Expected some or all of these to be errors doSomething(window); doSomething(42); doSomething('huh?');
Types with no members can be substituted by any type.
In this example, window
, 42
, and 'huh?'
all have the required members of a Thing
(there are none).
In general, you should never find yourself declaring an interface
with no properties.
I wrote the following code and expected an error:
type SomeUrl = string; type FirstName = string; let x: SomeUrl = "http://www.typescriptlang.org/"; let y: FirstName = "Bob'; x = y; // Expected error
Type aliases are simply aliases -- they are indistinguishable from the types they refer to.
A workaround involving intersection types to make "branded primitives" is possible:
// Strings here are arbitrary, but must be distinct
type SomeUrl = string & {'this is a url': {}};
type FirstName = string & {'person name': {}};
// Add type assertions
let x = <SomeUrl>'';
let y = <FirstName>'bob';
x = y; // Error
// OK
let xs: string = x;
let ys: string = y;
xs = ys;
You'll need to add a type assertion wherever a value of this type is created.
These can still be aliased by string
and lose type safety.
I would like the following code to produce an error:
interface ScreenCoordinate { x: number; y: number; } interface PrintCoordinate { x: number; y: number; } function sendToPrinter(pt: PrintCoordinate) { // ... } function getCursorPos(): ScreenCoordinate { // Not a real implementation return { x: 0, y: 0 }; } // This should be an error sendToPrinter(getCursorPos());
A possible fix if you really want two types to be incompatible is to add a 'brand' member:
interface ScreenCoordinate {
_screenCoordBrand: any;
x: number;
y: number;
}
interface PrintCoordinate {
_printCoordBrand: any;
x: number;
y: number;
}
// Error
sendToPrinter(getCursorPos());
Note that this will require a type assertion wherever 'branded' objects are created:
function getCursorPos(): ScreenCoordinate {
// Not a real implementation
return <ScreenCoordinate>{ x: 0, y: 0 };
}
See also #202 for a suggestion tracking making this more intuitive.
I want to write some code like this:
interface SomeInterface { name: string; length: number; } interface SomeOtherInterface { questions: string[]; } function f(x: SomeInterface|SomeOtherInterface) { // Can't use instanceof on interface, help? if (x instanceof SomeInterface) { // ... } }
TypeScript types are erased (https://en.wikipedia.org/wiki/Type_erasure) during compilation. This means there is no built-in mechanism for performing runtime type checks. It's up to you to decide how you want to distinguish objects. A popular method is to check for properties an on object. You can use user-defined type guards to accomplish this:
function isSomeInterface(x: any): x is SomeInterface {
return typeof x.name === 'string' && typeof x.length === 'number';
function f(x: SomeInterface|SomeOtherInterface) {
if (isSomeInterface(x)) {
console.log(x.name); // Cool!
}
}
I wrote some code like this:
let x: any = true; let y = <string>x; // Expected: runtime error (can't convert boolean to string)
or this
let a: any = 'hmm'; let b = a as HTMLElement; // expected b === null
TypeScript has type assertions, not type casts.
The intent of <T>x
is to say "TypeScript, please treat x
as a T
", not to perform a typesafe runtime conversion.
Because types are erased, there is no direct equivalent of C#'s expr as
type or (type)expr
syntax.
I wrote some code like this and expected an error:
let myFunc: (number) => string = (n) => 'The number in hex is ' + n.toString(16); // Expected error because boolean is not number console.log(myFunc(true));
Parameter names in function types are required.
The code as written describes a function taking one parameter named number
of type any
.
In other words, this declaration
let myFunc: (number) => string
is equivalent to this one
let myFunc: (number: any) => string
To avoid this problem, turn on the noImplicitAny
flag, which will issue a warning about the implicit any
parameter type.
TODO: Port content from here
I wrote some code like this and got an unexpected error:
function f({x: number}) { // Error, x is not defined? console.log(x); }
Destructuring syntax is counterintuitive for those accustomed to looking at TypeScript type literals.
The syntax f({x: number})
declares a destructuring from the property x
to the local number
.
Looking at the emitted code for this is instructive:
function f(_a) {
// Not really what we were going for
var number = _a.x;
}
To write this code correctly, you should write:
function f({x}: {x: number}) {
// OK
console.log(x);
}
If you can provide a default for all properties, it's preferable to write:
function f({x = 0}) {
// x: number
console.log(x);
}
I wrote some code like this and expected an error:
class Empty { /* empty */ } var e2: Empty = window;
See the question "Why are all types assignable to empty interfaces?" in this FAQ.
What explains the difference between these two lines of code?
class Alpha { x: number }
class Bravo { x: number }
class Charlie { private x: number }
class Delta { private x: number }
let a = new Alpha(), b = new Beta(), c = new Charlie(), d = new Delta();
a = b; // OK
c = d; // Error
In TypeScript, classes are compared structurally.
The one exception to this is private
and protected
members.
When a member is private or protected, it must originate in the same declaration to be considered the same as another private or protected member.
I wrote some code like this:
class MyClass { x = 10; someCallback() { console.log(this.x); // Prints 'undefined', not 10 this.someMethod(); // Throws error "this.method is not a function" } someMethod() { } } let obj = new MyClass(); window.setTimeout(obj.someCallback, 10);
Synonyms and alternate symptoms:
- Why are my class properties
undefined
in my callback?- Why does
this
point towindow
in my callback?- Why does
this
point toundefined
in my callback?- Why am I getting an error
this.someMethod is not a function
?- Why am I getting an error
Cannot read property 'someMethod' of undefined
?
In JavaScript, the value of this
inside a function is determined as follows:
- Was the function the result of calling
.bind
? If so,this
is the first argument passed tobind
- Was the function directly invoked via a property access expression
expr.method()
? If so,this
isexpr
- Otherwise,
this
isundefined
(in "strict" mode), orwindow
in non-strict mode
The offending problem is this line of code:
window.setTimeout(obj.someCallback, 10);
Here, we provided a function reference to obj.someCallback
to setTimeout
.
The function was then invoked on something that wasn't the result of bind
and wasn't directly invoked as a method.
Thus, this
in the body of someCallback
referred to window
(or undefined
in strict mode).
Solutions to this are outlined here: http://stackoverflow.com/a/20627988/1704166
I wrote some code like this and don't understand the error I'm getting:
class MyClass { someMethod() { } } var x: MyClass; // Cannot assign 'typeof MyClass' to MyClass? Huh? x = MyClass;
It's important to remember that in JavaScript, classes are just functions.
We refer to the class object itself -- the value MyClass
-- as a constructor function.
When a constructor function is invoked with new
, we get back an object that is an instance of the class.
So when we define a class, we actually define two different types.
The first is the one referred to by the class's name; in this case, MyClass
.
This is the instance type of the class.
It defines the properties and methods that an instance of the class has.
It's the type returned by invoking the class's constructor.
The second type is anonymous.
It is the type that the constructor function has.
It contains a construct signature (the ability to be invoked with new
) that returns an instance of the class.
It also contains any static
properties and methods the class might have.
This type is typically referred to as the "static side" of the class because it contains those static members (as well as being the constructor for the class).
We can refer to this type with the type query operator typeof
.
The typeof
operator (when used in a type position) expresses the type of an expression.
Thus, typeof MyClass
refers to the type of the expression MyClass
- the constructor function that produces instances of MyClass
.
See #1617 for this and other initialization order questions
TODO: Write up common symptoms of declare class
/ interface
confusion.
See http://stackoverflow.com/a/14348084/1704166
What does this code mean?
class Foo { /* ... */ } interface Bar extends Foo { }
This makes a type called Bar
that has the same members as the instance shape of Foo
.
However, if Foo
has private members, their corresponding properties in Bar
must be implemented
by a class which has Foo
in its heritage.
In general, this pattern is best avoided, especially if Foo
has private members.
I wrote some code like this:
/** file1.ts **/ class Alpha { /* ... */ } /** file2.ts **/ class Bravo extends Alpha { /* ... */ }I'm seeing a runtime error in
__extends
:Uncaught TypeError: Alpha is not defined
The most common cause of this is that your HTML page includes a <script>
tag for file2.ts, but not file1.ts.
Add a script tag for the base class's output before the script tag for the derived class.
I wrote some code:
/** file1.ts **/ class Alpha { /* ... */ } /** file2.ts **/ class Bravo extends Alpha { /* ... */ }I'm seeing a runtime error in
__extends
:Uncaught TypeError: Cannot read property 'prototype' of undefined
This can happen for a few reasons.
The first is that, within a single file, you defined the derived class before the base class. Re-order the file so that base classes are declared before the derived classes.
If you're using --out
, the compiler may be confused about what order you intended the files to be in.
See the section "How do I control file ordering..." in the FAQ.
If you're not using --out
, your script tags in the HTML file may be the wrong order.
Re-order your script tags so that files defining base classes are included before the files defining the derived classes.
Finally, if you're using a third-party bundler of some sort, that bundler may be ordering files incorrectly. Refer to that tool's documentation to understand how to properly order the input files in the resulting output.
I wrote some code and expected an error:
interface Something<T> { name: string; } let x: Something<number>; let y: Something<string>; // Expected error: Can't convert Something<number> to Something<string>! x = y;
TypeScript uses a structural type system.
When determining compatibility between Something<number>
and Something<string>
, we examine each member of each type.
If each member of the types are compatible, then the type are compatible as well.
Because Something<T>
doesn't use T
in any member, it doesn't matter what type T
is.
In general, you should never have a type parameter which is unused. The type will have unexpected compatibility (as shown here) and will also fail to have proper generic type inference in function calls.
I want to write some code like this:
function doSomething<T>(x: T) { // Can't find name T? let xType = typeof T; let y = new xType(); // Same here? if(someVar instanceof typeof T) { } }
Generics are erased during compilation.
This means that there is no value T
at runtime inside doSomething
.
The normal pattern that people try to express here is to use the constructor function for a class either as a factory or as a runtime type check.
In both cases, using a construct signature and providing it as a parameter will do the right thing:
function create<T>(ctor: { new(): T }) {
return new ctor();
}
var c = create(MyClass); // c: MyClass
function isReallyInstanceOf<T>(ctor: { new(...args: any) => T }, obj: T) {
return obj instanceof ctor;
}
I wrote some code like this
import someModule = require('./myMod'); let x: someModule.SomeType = /* something */;and the emit looked like this:
// Expected to see "var someModule = require('./myMod');" here! var x = /* something */;
TypeScript assumes that module imports do not have side effects, so it removes module imports that aren't used in any expression. Depending on your exact scenario, there are a few workarounds.
You can simply reference the module. This is the most universal workaround. A single use will do:
import someModule = require('./myMod');
someModule; // Used for side effects
If you're using CommonJS (typically with Node), you can use require
directly while still using the import for type information:
import _someModule = require('./myMod');
const someModule: typeof _someModule = require('./myMod');
If you're using CommonJS and don't need type information, you can just invoke require
:
require('./myMod');
If you're using AMD, you can use the amd-depedency
tag:
/// <amd-depedency path="./myMod" />
import someModule = require('./myMod');
TODO: Port content from http://stackoverflow.com/questions/30357634/how-do-i-use-namespaces-with-typescript-external-modules
TODO: Write up common symptoms of enum
/ const enum
confusion.
See http://stackoverflow.com/questions/28818849/how-do-the-different-enum-variants-work-in-typescript
It depends what x
is.
If the type of x
was originally not even compatible with Foo
, then it wouldn't make much sense to narrow the type, so we don't.
More likely, you'll find yourself in this situation when x
had the type any
.
The motivating example for this is something like the following:
function doIt(x) {
if (x instanceof Object) {
// Assume 'x' is a well-known object which
// we know how to handle specifically
}
// Treat 'x' as a primitive
}
You'll see this type of code in TypeScript code that predates union types, or TypeScript code that's been ported over from JavaScript.
If we narrowed from any
to Object
then there's not much you could really do with x
.
Using any properties that aren't in Object
will lead to an error.
This is not just true of Object
, it's true of any other type with a defined set of properties.
TODO, but it is strongly related to the above section.
TODO: Answer. Also, what did we mean here?
TODO: Answer
I wrote some code like this. Why is there an error?
class Display extends React.Component<any, any> { render() { ... } } let SomeThing: Display = /* ... */; // Error here, isn't this OK? let jsx = <SomeThing />;
This is a confusion between the instance and static side of a class.
When React instantiates a component, it's invoking a constructor function.
So when TypeScript sees a JSX <TagName />
, it is validating that the result of constructing TagName
produces a valid component.
But by declaring let SomeThing: Display
, the code is indicating that SomeThing
is the class instance, not the class constructor.
Indeed, it would be a runtime error to write:
let SomeThing = new Display();
let jsx = <SomeThing />; // Not gonna work
The easiest fix is to use the typeof
type operator.
let SomeThing: typeof Display = /* ... */;
If I write code like this:
class Foo { private x = 0; increment(): number { this.x++; return x; } }You should emit code like this so that 'x' is truly private:
var Foo = (function () { var x = 0; function Foo() { } Foo.prototype.increment = function () { x++; return x; }; return Foo; })();
This code doesn't work. It creates a single private field that all classes share:
var a = new Foo();
a.increment(); // Prints 1
a.increment(); // Prints 2
var b = new Foo(); // Should not affect a
a.increment(); // Prints 1
If I write code like this:
class MyClass { method() { } }You should emit code like this so that I can't mess up
this
in in callbacks:var MyClass = (function () { function MyClass() { this.method = function() { } } return MyClass; })();
Two problems here.
First, the proposed behavior change is not in line with the ECMAScript specification. There isn't really anything else to be said on that front -- TypeScript must have the same runtime behavior as JavaScript.
Second, the runtime characteristics of this class are very surprising. Instead of allocating one closure per method, this allocates one closure per method per instance. This expensive in terms of class initialization cost, memory pressure, and GC performance.
TODO: Port content from #1617
TODO: Answer
I wrote a program:
/* myApp.ts */ function doSomething() { console.log('Hello, world!'); } doSomething();I compiled it with
tsc --module commonjs myApp.ts --out app.js
and rannode app.js
and got the expected output.Then I added an
import
to it:import fs = require('fs'); function doSomething() { console.log('Hello, world!'); } doSomething();Or added an
export
to it:export function doSomething() { console.log('Hello, world!'); } doSomething();And now nothing happens when I run
app.js
!
Modules -- those files containing top-level export
or import
statements -- are always compiled 1:1 with their corresponding js files.
The --out
option only controls where script (non-module) code is emitted.
In this case, you should be running node myApp.js
, because the module myApp.ts
is always emitted to the file myApp.js
.
This behavior has been fixed as of TypeScript 1.8; combining --out
and --module
is now an error for CommonJS module output.
TODO: Answer
This error occurs when you use the --declaration
flag because the compiler is trying to produce a declaration file that exactly matches the module you defined.
Let's say you have this code:
/// MyFile.ts
class Test {
// ... other members ....
constructor(public parent: Test){}
}
export let t = new Test("some thing");
To produce a declaration file, the compiler has to write out a type for t
:
/// MyFile.d.ts, auto-generated
export let t: ___fill in the blank___;
The member t
has the type Test
. The type Test
is not visible because it's not exported, so we can't write t: Test
.
In the very simplest cases, we could rewrite Test
's shape as an object type literal. But for the vast majority of cases, this doesn't work. As written, Test
's shape is self-referential and can't be written as an anonymous type. This also doesn't work if Test
has any private or protected members. So rather than let you get 65% of the way through writing a realistic class and then start erroring then, we just issue the error (you're almost certainly going to hit later anyway) right away and save you the trouble.
For some code examples, we'll use a hypothetical type hierarchy:
Animal
/ \
Dog Cat
That is, all Dog
s are Animal
s, all Cat
s are Animal
s, but e.g. a function expecting a Dog
cannot accept an argument of type Cat
.
If you want to try these examples in the TypeScript Playground, start with this template:
interface Animal {
move(): void;
}
interface Dog extends Animal {
woof: string;
}
interface Cat extends Animal {
meow: string;
}
Other examples will use DOM types like HTMLElement
and HTMLDivElement
to highlight concrete real-world implications of certain behaviors.
Many answers relating to the type system make reference to Substitutability.
This is a principle that says if an object X
can be used in place of some object Y
, then X
is a subtype of Y
.
We also commonly say that X
is assignable to Y
(these terms have slightly different meanings in TypeScript, but the difference is not important here).
In other words, if I ask for a fork
, a spork
is an acceptable substitute because it has the same functions and properties of a fork
(three prongs and a handle).
TypeScript Language Basics
- Basic Types
- Interfaces
- Classes
- Namespaces and Modules
- Functions
- Generics
- Compiler Options
- tsconfig.json
- Integrating with Build Tools
- Nightly Builds
TypeScript Language Advanced
- Mixins
- Declaration Merging
- Type Inference
- Type Compatibility
- JSX
- Writing Declaration Files
- Typings for NPM packages
News
TypeScript Contributors
- Contributing to TypeScript
- TypeScript Design Goals
- Coding Guidelines
- Spec conformance testing
- Useful Links for TypeScript Issue Management
- Writing Good Design Proposals
- Compiler Internals
Building Tools for TypeScript
- Architectural Overview
- Using the Compiler API
- Using the Language Service API
- Dev Mode in Visual Studio
- Debugging Language Service in VS Code
FAQs