# TypeScript
Développé par Microsoft
C'est un sur-ensemble de JavaScript (c'est-à-dire que tout code JavaScript correct peut être utilisé avec TypeScript). Le code TypeScript est transcompilé en JavaScript, pouvant ainsi être interprété par n'importe quel navigateur web ou moteur JavaScript.
# auteurs / contributeurs
Anders Hejlsberg (fr) (GitHub profile)
Author of Turbo Pascal, chief architect of Delphi, lead architect of C#, conceptor of .NET framework and core developper and conceptor of TypeScript.
Program Manager of TypeScript
TypeScript lead dev
TypeScript dev (retired)
# articles
- blog développeurs sur msdn.org
- dev.com angular basé sur typescript
- angular-et-typescript-un-mariage-heureux : blog.xebia.fr
- definitive guide to typescript
- Top 10 Things to Know about TypeScript - www.developer.com - 20130227
- The TypeScript Tax, A Cost vs Benefit Analysis - Eric Elliott - 20190123
- TypeScript: validating external data - Rauschmayer - 20200609
TypeScript Evolution - Marius Schulz
34 articles about TS features from TS@2.0 to TS@2.8
TypeScript lead dev Ryan Cavanaugh about TypeScript and OOP :
"I'm using TypeScript so I have to write OOP code with classes" :: "I got a paintbrush from Home Depot so I have to paint my house orange"
# adoption
@swyx tweet about TS migration
React ecosystem libraries that have rewritten their core to @TypeScript (not just offering TypeScript support):
Next.js
@ReactNative CLI
React Router
@expo
Redux
@Yarnpkg
@fbjest
@storybookjs
@apollographql
@Gatsbyjs
# documentation
- Documentation - www.typescriptlang.org
- TypeScript/wiki -
github.com/Microsoft
- TypeScript/wiki/FAQ -
github.com/Microsoft
- TypeScript specifications
- angular typescript quickstart
# Difference between script as a file and script at runtime
Taken from this article from Charly Poly
# starters projects
samples - www.typescriptlang.org
TypeScript Node Starter - github.com/Microsoft
See in particular their Type Definition (
.d.ts
) Files management which give a lot of infos.
# tsconfig.json
tsconfig.json
official doc - www.typescriptlang.org
tsconfig.json
schema on json.schemastore.org
TypeScript Configuration - angular.io/guide
# compiler options
# --noImplicitAny
See noImplicitAny in TypeScript Deep Dive from basarat.gitbooks.io
By default this option is set to false
.
When a type is not defined by the programmer and when it is impossible to infer it, TypeScript infers an any
type, example :
function log(someArg) {
sendDataToServer(someArg);
}
// What arg is valid and what isn't?
log(123);
log('hello world');
2
3
4
5
6
7
8
9
When set to true
, the compiler raises an error in that case.
It forces the programmer to explicitly set the type and at least the any
type which in fact remove type checking (it allows anything).
# --strictNullChecks
See Nullable Types section in Advanced Types from TS official handbook
Introduced in TS 2.0.
By default this option is set to false
for retro compat purposes.
In JavaScript and without this option in TypeScript, undefined
and null
are assignable to anything. We can do this :
let toto = 'toto';
toto = null;
2
3
4
Or in TypeScript :
let toto: string = 'toto';
toto = null;
2
3
4
With this is option set to true
, it is not possible anymore.
The intent is to avoid null pointers exceptions.
If the option is set, the programmer needs to explicitly declare when a value can be undefined
or null
like this :
let toto: string | null = 'toto';
toto = null;
toto = undefined; // Still raise error, should have declare toto as 'let toto: string | null | undefined;'
2
3
4
5
6
# optional parameters
If the option is set and an optional parameter is used, the type is implicitly considered to be an union with undefined
:
function f(x: number, y?: number) {
return x + (y || 0);
}
f(1, 2); // OK
f(1); // OK
f(1, undefined); // OK
f(1, null); // error, 'null' is not assignable to 'number | undefined'
2
3
4
5
6
7
8
9
# declaration files
Introduction - www.typescriptlang.org/docs/handbook
Consumption of *.d.ts
files - www.typescriptlang.org/docs/handbook
install a
@types/<lib>
package or if the lib itself include a"types"
prop in itspackage.json
it's already there.See DefinitelyTyped @
github.com
for@types
consumption and TS publishing doc for"types"
prop.Ex : Node.js typings are there.
Ambient Declarations - basarat.gitbooks.io/typescript
Getting started with TypeScript type definitions - medium.com/@jonjam - 20171113
# tools
dts-gen - github.com/Microsoft
# best practices
# managing export
/ import
export default
considered harmful
practice of creating an
index.ts
file inside a directory to re-export files (imply to avoidexport default
) allows destructured import :import { Foo, Bar, Baz } from '../demo'; // demo/index.ts is implied
# Linters
# TSLint
# TSLint deprecated soon
TSLint in 2019 - medium.com/palantir - 20190219
TSLint will be deprecated asap in favor of typescript-eslint.
# typescript-eslint
https://github.com/typescript-eslint
typescript-eslint/typescript-eslint - github.com
Monorepo for all the tooling which enables ESLint to support TypeScript https://typescript-eslint.io/
# videos
Migration progressive (passer les
.js
en.ts
suffit à migrer surtypescript
et inversement pour rollback)Facilité apprentissage (juste le fait de typer)
Compatible
ES6
(ajoute des polyfills, 90% de babel, donc autant faireES5
->typescript
plutôt queES5
->ES6
->TS
Refactoring simplifié du fait des types (erreurs à la compilation)
Assitance IDE meilleure (Webstorm nickel)
# erreurs tscompiler
Diagnostics are categorized into general ranges. If adding a new diagnostic message, use the first integral number greater than the last used number in the appropriate range.
1000 range for syntactic messages 2000 for semantic messages 4000 for declaration emit messages 5000 for compiler options messages 6000 for command line compiler messages 7000 for noImplicitAny messages
# Erreurs courantes après migration ES5/ES6 vers TS
TS2339: Property 'xxx' does not exist on type 'Yyyy'.
Cf issue 6373 : Getting error TS2339: Property does not exist on type for a valid ES6 class
Cf issue 2606 : ES6 should be valid TypeScript
class Car {
constructor(weight) {
this.weight = weight;
}
}
2
3
4
5
is invalid ts code, it will output Error:(3, 14) TS2339: Property 'weight' does not exist on type 'Car'.
class Car {
weight: number;
constructor(weight: number) {
this.weight = weight;
}
}
2
3
4
5
6
7
is required by TS.
Anyway it is a non-blocker to generate the target JavaScript bundle.
If the input code is syntactically correct (prior to type checking) then it can generate ES output, and it is "valid" TS. At this first level, TS is a superset of ES, in that the set of valid TS programs is larger than the set of valid ES programs (because it includes all the valid ES programs plus those with type annotations).
The second level is type-correctness, which is what your error is complaining about. At this level, TS can act as a subset of ES: some valid ES programs, such as your example, are not type-correct TS programs.
error TS2380: 'get' and 'set' accessor must have the same type.
See TypeScript/issues/4087 and TypeScript/issues/2521
A classical pattern in JS with class to define a model is :
class MyClass {
constructor(value) {
this._myDate = value;
}
get myDate() {
return this._myDate;
}
set myDate(value) {
this._myDate = moment(value);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
The accessors are used to refine / format the data set to the instance.
But in TypeScript accessors must have the same type, and when converted to TS the compiler raise an error :
class MyClass {
private _myDate: moment.Moment;
get myDate(): moment.Moment {
return this._myDate;
}
set myDate(value: Date | moment.Moment) {
this._myDate = moment(value);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
The workaround is to create a dedicated function but we loose the instance.myDate = new Date();
usage.
class MyClass {
private _myDate: moment.Moment;
get myDate(): moment.Moment {
return this._myDate;
}
set myDate(value: moment.Moment) {
assert.fail('Setter for myDate is not available. Please use: setMyDate() instead');
}
setMyDate(value: Date | moment.Moment) {
this._myDate = moment(value);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Another workaround would be to type _myDate
with Date | moment.Moment
but we loose a lot of type checking here.
See cons TS2380
rule arguments :
When TypeScript limits JavaScript, it becomes more of a nuisance than an advantage. Isn't TypeScript meant to help developers communicate with each other?
Also, setters are called Mutators for a reason. If I wouldn't need any kind of conversion, I wouldn't use a setter, I would set the variable by myself.
impact with Angular and components inputs from templates.
See pros TS2380
rule arguments :
kitsonk (TypeScript contributor)
IMO, ever since JavaScript allowed accessors, people have potentially created confusing APIs with them. I personally find it confusing that something on assignment magically changes to something else. Implicit anything, especially type conversion, is the bane of JavaScript IMO. It is exactly the flexibility that causes problems.
mhegazy (TypeScript contributor)
After thinking about this some more. i think the issue here is really the complexity of the implementation.
He flagged issue as "Too Complex" and "Design Limitation" labels after that post then closed the issue 2521.
kitsonk (TypeScript contributor)
The labels on the issue indicate it is a design limitation and the implementation would be considered too complex, which essentially means that if someone has a super compelling reason why this should be the case, it is not going anywhere.
# features
# Control Flow Based Type Analysis
See what's new in TS@2.0 - www.typescriptlang.org
TypeScript 2.0: Control Flow Based Type Analysis - mariusschulz.com/blog - 20160930
With TypeScript 2.0, the type checker analyses all possible flows of control in statements and expressions to produce the most specific type possible (the narrowed type) at any given location for a local variable or parameter that is declared to have a union type.
# conditional types
Added in TypeScript 2.8 see release notes.
See Conditional Types section in Advanced Types from TS official handbook
How are you using conditional types? - www.reddit.com/r/typescript - 20181010
Super nice answer here
- Conditional types in TypeScript -
artsy.github.io/blog
- 20181121 (Super nice article with practical examples)
Conditional types probably aren't something you'll write every day, but you might end up using them indirectly all the time. That's because they're great for 'plumbing' or 'framework' code, for dealing with API boundaries and other behind-the-scenes kinda stuff.
# type assertion
See Type assertions @ Basic Types - www.typescriptlang.org
Sometimes you’ll end up in a situation where you’ll know more about a value than TypeScript does.
Type assertions are a way to tell the compiler "trust me, I know what I’m doing."
A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler.
The "angle-bracket" syntax :
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
2
3
The as
syntax :
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
2
3
The first is the legacy one and the second has been added to be compatible with JSX (TSX with TypeScript).
The first one clashed with JSX syntax.
The second is now considered to be the syntax to use.
type-assertion - basarat.gitbooks.io/typescript
# type aliases
See Type aliases @ Types Aliases - www.typescriptlang.org
An interface or literal type with just a call signature can be written as a function type.
Interface vs Type alias in TypeScript - Martin Hochel - 20180312
what’s the difference between using
type
andinterface
for defining compile time types within TypeScriptofficial documentation is obsolete since TS@2.1 :
- errors messages display type alias name correctly
- types aliases can be extended (extended by an
interface
or implemented by aclass
)- types aliases can be used for type alias extension via intersection operator
&
# type guards
See Type Guards and Differentiating Types - www.typescriptlang.org
A type guard is some expression that performs a runtime check that guarantees the type in some scope.
Checking the type of an object in Typescript: the type guards - medium.com/ovrsea - 20181122
What does the is
keyword do in typescript? - stackoverflow.com - 20161017
function isString(test: any): test is string{
return typeof test === 'string';
}
function example(foo: any){
if(isString(foo)){
console.log('it is a string' + foo);
console.log(foo.length); // string function
}
}
example('hello world');
2
3
4
5
6
7
8
9
10
11
Inside a type guard function, we can check :
- that a property exist with the
in
keyword ('propname' in foo
wherefoo
is the object passed to the type guard function to check onto) - that a property is from the
typeof
number
,string
,boolean
, orsymbol
and nothing more.
It is impossible to check if a property is from the typeof
a custom type because there is not the required meta data available at runtime.
# unknown
type
The purpose of the unknown
type is to force the developper to check the structure with type guards or assert the type with type assertion.
See what's new in TS@3.0 - www.typescriptlang.org
unknown
is the type-safe counterpart ofany
. Anything is assignable tounknown
, butunknown
isn’t assignable to anything but itself andany
without a type assertion or a control flow based narrowing. Likewise, no operations are permitted on anunknown
without first asserting or narrowing to a more specific type.
TypeScript 3.0: Exploring Tuples and the Unknown Type - auth0.com/blog - 20180821
We can use an
unknown
type if and only if we perform some form of checking on its structure. We can either check the structure of the element we want to use or we can use type assertion to tell TypeScript that we are confident about the type of the value
# Dependency Injection with TypeScript
# Using the classical functional style
Using type aliases to define function types.
In vanilla JavaScript it is dead simple, we use curried function and partial application :
// dependency function without deps
const doSomethingElse = (arg) => {
// impl ...
}
export default doSomethingElse;
// function with dependency
const makeDoSomething = ({ doSomethingElse }) => (arg) => {
// impl ...
const value = doSomethingElse(111);
// impl ...
}
export default makeDoSomething;
// caller
import doSomethingElse from './do-something-else'
import makeDoSomething from './make-do-something'
const doSomething = makeDoSomething({ doSomethingElse });
doSomething('toto');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
In TypeScript we can use the same pattern, and we can type dependencies using Type Aliases.
We could prefer type aliases instead of interfaces because of TSLint callable-types
rule.
The same example as above in TypeScript becomes :
// dependency function without deps
export type DoSomethingElseFnType = (arg: number) => number;
export function doSomethingElse(arg: number): number {
// impl ...
}
// function with dependency
import { DoSomethingElseFnType } from './do-something-else';
export type = DoSomethingFnType = (arg: string) => string;
export function makeDoSomething(deps: { doSomethingElse: DoSomethingElseFnType}): DoSomethingFnType {
return (arg: string): string => {
// impl ...
// you reach your dependency through deps destructuring and benefit from typing :
const value = deps.doSomethingElse(111);
// impl ...
}
}
// caller
import { doSomethingElse } from './do-something-else';
import { DoSomethingFnType, makeDoSomething } from './make-do-something';
const doSomething: DoSomethingFnType = makeDoSomething({ doSomethingElse });
doSomething('toto');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Using POO style by hand
You don't need any typing here, because obviously a class
is a type.
// dependency class without deps
export class SomethingElse {
public static makeSomethingElse(): SomethingElse {
return new SomethingElse();
}
private constructor() {
}
public doSomethingElse(arg: number): number {
// impl ...
}
}
// class with dependency
import { SomethingElse } from './something-else';
export class Something {
public static makeSomething(deps: {somethingElse: SomethingElse}): Something {
return new Something(deps);
}
private somethingElse: SomethingElse;
private constructor(deps: {somethingElse: SomethingElse}) {
this.somethingElse = deps.somethingElse;
}
public doSomething(arg: string): string {
const value = this.somethingElse.doSomethingElse(111);
// impl ...
}
}
// caller
import { SomethingElse } from './something-else';
import { Something } from './do-something';
const somethingElse: SomethingElse = SomethingElse.makeSomethingElse();
const something: Something = Something.makeSomething({ somethingElse });
something.doSomething('toto');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Using POO style with decorators
See decorators from www.typescriptlang.org/docs/handbook.
Dependency Injection in TypeScript - 20180205
InversifyJS - github.com/inversify
A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
# Tricks
# Nominal typing
Nominal typing techniques in TypeScript - michalzalecki.com
- 20171227 - Michal Zalecki
- Approach #1: Class with a private property
class USD {
private __nominal: void;
constructor(public value: number) {};
}
class EUR {
private __nominal: void;
constructor(public value: number) {};
}
const usd = new USD(10);
const eur = new EUR(10);
function gross(net: USD, tax: USD) {
return { value: net.value + tax.value } as USD;
}
gross(usd, usd); // ok
gross(eur, usd); // Error: Types have separate declarations of a private property '__nominal'.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- Approach #2: Brands
interface USD {
_usdBrand: void;
value: number;
}
interface EUR {
_eurBrand: void;
value: number;
}
let usd: USD = { value: 10 } as USD;
let eur: EUR = { value: 10 } as EUR;
function gross(net: USD, tax: USD) {
return { value: net.value + tax.value } as USD;
}
gross(usd, usd); // ok
gross(eur, usd); // Error: Property '_usdBrand' is missing in type 'EUR'.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- Approach #3: Intersection types
class Currency<T extends string> {
private as: T;
}
type USD = number & Currency<"USD">
type EUR = number & Currency<"EUR">
const usd = 10 as USD;
const eur = 10 as EUR;
function gross(net: USD, tax: USD) {
return (net + tax) as USD;
}
gross(usd, usd); // ok
gross(eur, usd); // Error: Type '"EUR"' is not assignable to type '"USD"'.
// ...
function ofUSD(value: number) {
return value as USD;
}
function ofEUR(value: number) {
return value as EUR;
}
const usd = ofUSD(10);
const eur = ofEUR(10);
function gross(net: USD, tax: USD) {
return ofUSD(net + tax);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- Approach #4: Intersection types and brands
type Brand<K, T> = K & { __brand: T }
type USD = Brand<number, "USD">
type EUR = Brand<number, "EUR">
const usd = 10 as USD;
const eur = 10 as EUR;
function gross(net: USD, tax: USD): USD {
return (net + tax) as USD;
}
gross(usd, usd); // ok
gross(eur, usd); // Type '"EUR"' is not assignable to type '"USD"'.
2
3
4
5
6
7
8
9
10
11
12
13
14
This PR in typescript repo will maybe land.
# error typing
Get a catch block error message with TypeScript
The default type for the variable exposed by a catch
clause is unknown
because in JavaScript we can throw anything.
So the TS compiler emit this error :
Catch clause variable type annotation must be 'any' or 'unknown' if specified. ts(1196)
When we try to type a catch clause like this :
try {
throw new Error('Oh no!')
} catch (error: Error) {
// we'll proceed, but let's report it
reportError({message: error.message})
}
2
3
4
5
6
So, we cannot type a variable in a catch
clause. We need to check type variable type manually :
try {
throw new Error('Oh no!')
} catch (error) {
let message = 'Unknown Error'
if (error instanceof Error) message = error.message
// we'll proceed, but let's report it
reportError({message})
}
2
3
4
5
6
7
8