TypeScript is
a typed superset of JavaScript
that compiles to plain JavaScript.
function greeter(person) {
return "Hello, " + person;
}
greeter('Michel');
// Hello, Michel
function greeter(person) {
return "Hello, " + person;
}
greeter({ firstname: 'Michel', lastname: 'Michel' });
// Hello, [object Object]
On ajoute le typage (TypeScript)
function greeter(person: string) {
return "Hello, " + person;
}
greeter('Michel');
// Hello, Michel
function greeter(person: string) {
return "Hello, " + person;
}
greeter({ firstname: 'Michel', lastname: 'Michel' });
// Argument of type '{ firstname: string; lastname: string; }'
// is not assignable to parameter of type 'string'.
Attention !
Le code compilé reste du JavaScript "classique".
function greeter(person) {
return "Hello, " + person;
}
greeter('Michel');
function nbLetter(str: string): number {
return str.length;
}
const nbLetter = (str: string): number => str.length;
TypeScript, c'est PHPStan pour JavaScript
let list: number[] = [1, 2, 3];
ou alors la forme plus explicite :
let list: Array<number> = [1, 2, 3];
function printLabel(labeledObj: { label: string }) {
console.log(labeledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
👍
"myObj" a bien une clé "label" de type "string"
interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
interface SquareConfig {
width?: number;
}
function createSquare(config: SquareConfig): {area: number} {
let newSquare = { area: 100 };
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({});
Une classe peut imlémenter une interface
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
constructor(h: number, m: number) { }
setTime(d: Date) {
this.currentTime = d;
}
}
class Human {
// public par défaut
firstname: string;
// idem
public lastname: string;
// accessible aux enfants uniquement, mais en JS ça reste public
protected religion: string;
// accessible à personne, mais en JS ça reste public, à éviter
private sex: string;
// fonctionnalité ECMAScript, vraiment privé, à privilégier
#salary: number;
}
function buildName(firstName, lastName) {
console.log(firstName, lastName);
}
buildName(); // log : undefined, undefined
function buildName(firstName: string, lastName?: string) {
console.log(firstName, lastName);
}
buildName(); // Erreur
buildName('Michel'); // OK, log: Michel, undefined
buildName('Michel', 'Michel'); // OK
buildName('Michel', 'Michel', 'Michel'); // Erreur
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string): boolean {
let result = source.search(subString);
return result > -1;
}
function callMe(fn: SearchFunc) {
// ...
}
let mySearch = function(source: string, subString: string): boolean {
let result = source.search(subString);
return result > -1;
}
function callMe(fn: (source: string, subString: string) => boolean) {
// ...
}
function doWeirdStuff(x: number): number;
function doWeirdStuff(x: string): boolean;
function doWeirdStuff(x): any {
if (typeof x === 'number') {
return x * 2;
}
return x.length > 3;
}
let n = doWeirdStuff(8); // n est de type "number"
let b = doWeirdStuff('this is weird'); // n est de type "boolean"
function identity(arg) {
return arg;
}
function identity(arg: number): number {
return arg;
}
soit noter les types en "any"
function identity(arg: any): any {
return arg;
}
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString");
// ^ = let output: string
function identity<T>(arg: T): T {
return arg;
}
let output = identity("myString");
// ^ = let output: string
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {
return x + y;
};
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity(3);
// Argument of type 'number' is not assignable
// to parameter of type 'Lengthwise'.
loggingIdentity({ length: 10, value: 3 }); // OK
interface Animal {
name: string
}
interface Bear extends Animal {
honey: boolean
}
type Animal = {
name: string
}
type Bear = Animal & {
honey: Boolean
}
import PropTypes from 'prop-types';
import React from 'react';
function Controls({ zoomIn, zoomOut }) {
return (
<div>
<button onClick={zoomIn}>+</button>
<button onClick={zoomOut}>−</button>
</div>
);
}
Controls.propTypes = {
zoomIn: PropTypes.func.isRequired,
zoomOut: PropTypes.func.isRequired,
};
-import PropTypes from 'prop-types';
import React from 'react';
-function Controls({ zoomIn, zoomOut }) {
+function Controls({ zoomIn, zoomOut }: ControlsProps): React.ReactElement {
return (
// ...
);
}
-Controls.propTypes = {
- zoomIn: PropTypes.func.isRequired,
- zoomOut: PropTypes.func.isRequired,
-};
+type ControlsProps = {
+ zoomIn: () => void;
+ zoomOut: () => void;
+};
import React, { Component, ReactElement } from 'react';
type State = {
counter: number;
}
type Props = {
reset: () => void;
seatId: number | null;
};
class CountThisSeat extends Component<Props, State> {
static defaultProps = {
seatId: null,
};
constructor(props: Props) {
super(props);
this.state = {
counter: 0,
}
}
render(): ReactElement {
// render component, it should be the same as JavaScript version
}
}
const SEND_MESSAGE = 'SEND_MESSAGE';
const LOGIN = 'LOGIN';
type SendMessageAction = {
type: typeof SEND_MESSAGE;
message: string;
}
type LoginAction = {
type: typeof LOGIN;
username: string;
}
type AppActionTypes = SendMessageAction | LoginAction;
type AppState = {
messages: Array<string>
loggedId: boolean;
username?: string;
}
const initialState: AppState = {
messages: [],
loggedId: false,
}
function appReducer(state = initialState, action: AppActionTypes) {
switch (action.type) {
// ...
}
}
// TypeScript infers that this function is returning SendMessageAction
export function sendMessage(newMessage: string): AppActionTypes {
return {
type: SEND_MESSAGE,
payload: newMessage
}
}
export function login(username: string): AppActionTypes {
return {
type: LOGIN,
username,
}
}
type ReduxStateProps = {
messages: Array<string>;
}
type DispatchProps = {
sendMessage: (message: string): AppActionTypes;
}
type OwnProps = {
theme: string;
}
type Props = ReduxStateProps & DispatchProps & OwnProps;
function SomeComponent(props: Props): ReactElement {
return (
// ...
);
}
// avec null
type Props = { id: null | number };
function constructor ({ id = null }: Props) {}
// avec undefined
type Props = { id?: number };
function constructor ({ id }: Props) {}
// imaginez ce composant
function Foo({ eventDateId }) {}
Foo.defaultProps = {
eventDateId: null,
};
Foo.propTypes = {
eventDateId: PropTypes.number,
};
// Ceci est plutôt faux. La vrai definition est:
Foo.defaultProps = {
eventDateId: null,
};
Foo.propTypes = {
eventDateId: PropTypes.oneOfType(
PropTypes.number,
PropTypes.null,
),
};
// et du coup en TypeScript:
Foo.defaultProps = {
eventDateId: null,
};
Foo.propTypes = {
eventDateId: number | null,
};
// là aussi, mieux avec `undefined`
Foo.defaultProps = {
eventDateId: undefined,
};
Foo.propTypes = {
eventDateId?: number,
};
Convertir les types TS en prop-types ? babel-plugin-typescript-to-proptypes
Des questions ?