Typescript is based on javascript with ‘weak’ enhancements
Preparation stage
- TSC compiles ts files: TSC *. Ts compiles ts files to JS files, and Node runs js files
sudo npm install typescript -g / / MAC installation
tsc --version // Check whether the installation is successful
Copy the code
- Ts-node runs ts files directly: ts-node *. Ts == node *.js
sudo npm install ts-node -g / / MAC installation
Copy the code
A simple example
function greet(person: string) {
console.log("Hello, " + person);
}
Copy the code
The base type
Just like Javascript, you just uppercase the first letter
The number type is a floating point number just like the number in JS.
In addition to supporting decimal and hexadecimal literals, TypeScript supports binary and octal literals introduced in ECMAScript 2015.
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
Copy the code
An array T []
There are two ways to define an array.
- First, the element type can be followed by [] to represent an array of elements of that type:
let list: number[] = [1.2.3];
Copy the code
- The second way is to use Array generics, Array< element type > :
let list: Array<number> = [1.2.3];
Copy the code
Tuples Tuple
// Declare a tuple type
let x: [string.number];
// Initialize it
x = ['hello'.10]; // OK
// Initialize it incorrectly
x = [10.'hello']; // Error
Copy the code
After Typescript2.7 (in the FixedLengthTuples section), a Tuple is defined as an array of limited length.
interface NumStrTupleextendsArray{
0:number;
1:string;
length:2;//usingthenumericliteraltype'2'
}
Copy the code
So you can’t cross the line anymore.
Enumeration enum
Enum types complement the JavaScript standard data types. As in other languages such as C#, enumeration types can be used to give friendly names to a set of values.
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
Copy the code
By default, elements are numbered from 0. You can also manually specify the values of the members. For example, let’s change the above example to number from 1:
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
Copy the code
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
Copy the code
One of the conveniences provided by an enumerated type is that you can refer to its name by enumeration.
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];
console.log(colorName); // Display 'Green' because its value is 2 in the code above
Copy the code
Any
Sometimes we want to specify a type for variables whose type is not known at programming time. These values may come from dynamic content, such as user input or third-party code libraries. In this case, we don’t want the type checker to check these values and just pass them through compile-time checks. We can then mark these variables with type any:
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
Copy the code
In contrast to Object, a variable of type Object only allows you to assign arbitrary values to it – but can’t call arbitrary methods on it, even if it does have them:
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
Copy the code
The any type is also useful when you only know part of the data type. For example, you have an array that contains different types of data:
let list: any[] = [1.true."free"];
list[1] = 100;
Copy the code
Void
In a way, void is like the opposite of any; it means there is no type at all. When a function returns no value, you usually see the return type void:
function warnUser() :void {
console.log("This is my warning message");
}
Copy the code
Declaring a void variable doesn’t do much good, because you can only assign undefined and null to it:
let unusable: void = undefined;
Copy the code
Null, and Undefined
In TypeScript, undefined and null have their own types called undefined and NULL, respectively. Like void, their own types are not very useful:
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
Copy the code
By default null and undefined are subtypes of all types. This means that you can assign null and undefined to variables of type number.
However, when you specify strictNullChecks, null and undefined can only be assigned to void and their respective checks. This avoids many common problems. Maybe somewhere you want to pass in a string or null or undefined, you can use the combined type string | null | is undefined.
Never
The never type represents the types of values that never exist. For example, the never type is the return type of function expressions or arrow function expressions that always throw an exception or have no return value at all; Variables can also be of type never, when they are bound by type protection that is never true.
object
Object represents a non-primitive type, that is, a type other than number, string, Boolean, symbol, NULL, or undefined.
Type assertion <>/as
Think of it as a cast in Java
Type assertions come in two forms,
- The first is the Angle bracket syntax:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
Copy the code
- The other is the as syntax:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
Copy the code
interface
- js
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = { size: 10.label: "Size 10 Object" };
printLabel(myObj);
Copy the code
- ts
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10.label: "Size 10 Object"};
printLabel(myObj);
Copy the code
The type checker does not check the order of attributes, as long as the corresponding attributes exist and the type is correct.
Optional properties?
interfaceSquareConfig { color? :string; width? :number;
}
function createSquare(config: SquareConfig) :{color: string; area: number} {
let newSquare = {color: "white".area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
Copy the code
An interface with optional attributes is similar to a normal interface definition, except that the optional attribute name definition is followed by one. Symbols.
Read-only property readonly
Some object properties can only change their value when the object is created. You can specify read-only properties with readonly before the property name:
interface Point {
readonly x: number;
readonly y: number;
}
Copy the code
A Point can be constructed by assigning an object literal. After assignment, x and y can never be changed.
let p1: Point = { x: 10.y: 20 };
p1.x = 5; // error!
Copy the code
TypeScript has the ReadonlyArray
type, which is similar to Array
except that all mutable methods are removed, thus ensuring that arrays can never be modified after they are created:
let a: number[] = [1.2.3.4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
Copy the code
On the last line of the code above, you can see that it is not possible to assign the entire ReadonlyArray to a normal array. But you can override it with type assertions:
a = ro as number[];
Copy the code
Additional attribute checks []:any
interfaceSquareConfig { color? :string; width? :number;
}
function createSquare(config: SquareConfig) :{ color: string; width: number } {
// ...
}
let mySquare = createSquare({ colour: "red".width: 100 });
Copy the code
CreateSquare receives an error if it passes too many object attributes.
interfaceSquareConfig { color? :string; width? :number;
[propName: string] :any;
}
function createSquare(config: SquareConfig) :SquareConfig {
// ...
}
let mySquare = createSquare({ colour: "red".width: 100 });
let mySquare = createSquare({color:"# 333".width:128.age:12.name:'test'})
Copy the code
Function types
interfaceSquareConfig { color? :string; width? :number;
}
function createSquare(config: SquareConfig) :{ color: string; width: number } {
// ...
}
let mySquare = createSquare({ colour: "red".width: 100 });
Copy the code
Continuing with the above code, the createSquare function is passed config and its type definition is SquareConfig. The function type is similar to that of the anonymous function, as follows:
interfaceSquareConfig {(color: string;
width: string;
):boolean;
}
let test = SquareConfig;
test = function(src: string, sub: string) :boolean {
let result = src.search(sub);
return result > -1;
}
mySearch('123456'.'7') // false
mySearch('123456'.'2') // true
Copy the code
Indexable type [x:string]:value
interface StringArray {
[index: number] :string;
}
let myArray: StringArray;
myArray = ["Bob"."Fred"];
let myStr: string = myArray[0];
Copy the code
TypeScript supports two types of index signatures: strings and numbers. Both types of indexes can be used, but the return value of the numeric index must be a subtype of the return value type of the string index. This is because when you index a number, JavaScript converts it to a string and then indexes the object. That is, indexing with 100 (a number) is the same as indexing with 100 (a string), so the two need to be consistent.
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
// Error: Use numeric string index, sometimes get completely different Animal!
interface NotOkay {
[x: number]: Animal;
[x: string]: Dog;
}
Copy the code
I came across a sinkhole here for me
Problem specification
- String index
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
interface NotOkay {
[x: string]: Animal;
}
let str : NotOkay;
str = {a: {name:'an'},b: {name:'bo'}};
let strArr : NotOkay[];
strArr = [{a: {name:'an'},b: {name:'bo'}}];
console.log(str.a,strArr[0]); // {name:'an'} {a:{name:'an'},b:{name:'bo'}}
Copy the code
- Index number
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
interface NotOkay {
[x: number]: Dog;
}
let num : NotOkay;
num = [{name:'an'.breed:'an'}, {name:'bo'.breed:'bo'}];
console.log(str[0]) // {name:'an',breed:'an'}
Copy the code
- At the same time there is
NumberNotOkay is a string type that can be indexed only
Class types
Implement interface
Like C# or Java interfaces, TypeScript can use them to explicitly force a class to conform to a contract.
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number){}}Copy the code
It is also possible to describe a method in an interface and implement it in a class, as in the following setTime methods:
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number){}}Copy the code
Interfaces describe the public parts of a class, not the public and private parts. It doesn’t help you check if the class has some private members.
The difference between the static and instance parts of a class
When you work with classes and interfaces, you should know that classes have two types: the type of the static part and the type of the instance. You’ll notice that you get an error when you use the constructor signature to define an interface and try to define a class that implements the interface:
interface ClockConstructor {
new (hour: number.minute: number);
}
class Clock implements ClockConstructor {
currentTime: Date;
constructor(h: number, m: number){}}Copy the code
This is because when a class implements an interface, only the instance part is type checked. Constructor exists in the static part of the class and is therefore outside the scope of inspection.
Therefore, we should directly manipulate the static part of the class. In the following example, we define two interfaces, ClockConstructor for the constructor and ClockInterface for the instance method. To make it easier to define a constructor, createClock, that creates an instance with the type passed in.
interface ClockConstructor {
new (hour: number.minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number) :ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number){}tick() {
console.log("beep beep"); }}class AnalogClock implements ClockInterface {
constructor(h: number, m: number){}tick() {
console.log("tick tock"); }}let digital = createClock(DigitalClock, 12.17);
let analog = createClock(AnalogClock, 7.32);
console.log(digital.tick(),analog.tick());
// beep beep
// tick tock
Copy the code
Because the first argument to createClock is of type ClockConstructor, in createClock(AnalogClock, 7, 32), AnalogClock is checked to see if it matches the constructor signature.
Inheritance interface extends
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
Copy the code
An interface can inherit multiple interfaces to create a composite interface of multiple interfaces.
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
Copy the code
Mixed type
Interfaces can describe the rich types in JavaScript. Because of the dynamic and flexible nature of JavaScript, sometimes you want an object to have multiple of the types mentioned above.
interface Counter {
(start: number) :string;
interval: number;
reset(): void;
}
function getCounter() :Counter {
let counter = <Counter>function (start: number) {}; counter.interval =123;
counter.reset = function () {};return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
Copy the code
Interface inheritance class
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select(){}}class TextBox extends Control {
select(){}}// Error: The "Image" type is missing the "state" attribute.
class Image implements SelectableControl {
select(){}}class Location {}Copy the code
In the example above, the SelectableControl contains all members of Control, including the private member state. Since state is a private member, only subclasses of Control can implement the SelectableControl interface. Because only subclasses of Control can have a private member state declared on Control, compatibility with private members is required.
Within the Control class, private member States are allowed to be accessed through instances of the SelectableControl. In fact, the SelectableControl interface is the same as the Control class with the Select method. The Button and TextBox classes are subclasses of the SelectableControl (because they both inherit from Control and have select methods), but the Image and Location classes are not.
class
Classes in TS are defined similarly to classes in Java
Public, private, and protected modifiers
The default for the public
private
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }}new Animal("Cat").name; // Error: 'name' is private.
Copy the code
TypeScript uses the structural type system. When comparing two different types, it doesn’t matter where they came from; if all members’ types are compatible, their types are considered compatible. The situation is different when comparing types with private or protected members. If one type contains a private member, the two types are compatible only if the other type also has such a private member, and they are both declared from the same place. Use this rule for protected members as well.
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }}class Rhino extends Animal {
constructor() { super("Rhino"); }}class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }}let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino;
animal = employee; // error: Animal is incompatible with Employee.
Copy the code
protected
Protected modifiers behave much like private modifiers, with one difference: protected members are still accessible in derived classes.
class Person {
protected name: string;
constructor(name: string) { this.name = name; }}class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name)
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is The ${this.name} and I work in The ${this.department}. `; }}let howard = new Employee("Howard"."Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); / / error
Copy the code
Note that you cannot use name outside of the Person class, but you can still access it through the instance method of the Employee class because Employee is derived from Person.
Constructors can also be marked protected. This means that the class cannot be instantiated outside the containing class, but can be inherited.
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }}// Employee can inherit Person
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is The ${this.name} and I work in The ${this.department}. `; }}let howard = new Employee("Howard"."Sales");
let john = new Person("John"); // Error: the constructor for 'Person' is protected.
Copy the code
readonly
Use the readonly keyword to make the property read-only. Read-only attributes must be initialized at declaration time or in a constructor.
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName; }}let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; / / error! Name is read-only.
Copy the code
Parameter properties
Parameter properties make it easy to define and initialize a member in one place. The above code combined with parameter attributes can be written as:
class Octopus {
readonly numberOfLegs: number = 8;
constructor(readonly name: string){}}Copy the code
accessor
TypeScript supports intercepting access to object members via getters/setters. It helps you effectively control access to object members.
Here’s how to rewrite a simple class to use get and set. First, let’s start with an example that doesn’t use accessors.
class Employee {
fullName: string;
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
console.log(employee.fullName);
}
Copy the code
You can set fullnames arbitrarily, which is very convenient, but can also cause trouble.
In this version, the user is checked to see if the password is correct before being allowed to change the employee information. Changed direct access to fullName to a set method that checks the password. A get method was also added to make the above example still work.
let passcode = "secret passcode";
class Employee {
private _fullName: string;
get fullName() :string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "secret passcode") {
this._fullName = newName;
}
else {
console.log("Error: Unauthorized update of employee!"); }}}let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
alert(employee.fullName);
}
Copy the code
Accessors have the following points to note:
- Accessors require that you set the compiler to output ECMAScript 5 or higher. Downgrading to ECMAScript 3 is not supported.
- Only with
get
Without aset
The accessor is automatically inferred toreadonly
. This is helpful when generating.d.ts files from code, because users who exploit this property will see that it is not allowed to change its value.
Static attribute static
So far, we’ve only talked about instance members of a class, attributes that are initialized only when the class is instantiated. We can also create static members of a class whose properties exist on the class itself rather than on an instance of the class. In this example, we define Origin as static because it is the property used by all grids. Every instance that wants to access this property must prefix origin with the class name. Just as we use the this. prefix on instance properties to access properties, here we use Grid. To access static properties.
class Grid {
static origin = {x: 0.y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number; }) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) {}}let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10.y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10.y: 10}));
Copy the code
An abstract class abstract
Abstract classes are used as base classes for other derived classes. They are generally not instantiated directly. Unlike interfaces, abstract classes can contain implementation details of members. The abstract keyword is used to define abstract classes and abstract methods within abstract classes.
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch... '); }}Copy the code
Abstract methods in abstract classes contain no concrete implementation and must be implemented in derived classes. The syntax of abstract methods is similar to that of interface methods. Both define the method signature but do not contain the method body. However, abstract methods must contain the abstract keyword and can contain access modifiers.
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log('Department name: ' + this.name);
}
abstract printMeeting(): void; // Must be implemented in a derived class
}
class AccountingDepartment extends Department {
constructor() {
super('Accounting and Auditing'); // Super () must be called in the constructor of a derived class
}
printMeeting(): void {
console.log('The Accounting Department meets each Monday at 10am.');
}
generateReports(): void {
console.log('Generating accounting reports... '); }}let department: Department; // Allows you to create a reference to an abstract type
department = new Department(); // Error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // Allows instantiation and assignment of an abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // Error: method does not exist in declared abstract class
Copy the code
Advanced techniques
The constructor
When you declare a class in TypeScript, you’re declaring many things at once. The first is the type of the instance of the class.
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting; }}let greeter: Greeter;
greeter = new Greeter("world");
console.log(greeter.greet());
Copy the code
A class has an instance part and a static part.
class Greeter {
static standardGreeting = "Hello, there";
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
}
else {
returnGreeter.standardGreeting; }}}let greeter1: Greeter;
greeter1 = new Greeter();
console.log(greeter1.greet());
let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());
Copy the code
In this case, Greeter1 is the same as we saw before. Instantiate the Greeter class and use this object. Same thing we saw before.
After that, use classes directly. A variable called greeterMaker is created. This variable holds the class or holds the class constructor. Then use typeof Greeter, which means take the typeof the class, not the typeof the instance. Or, more accurately, “tell the type of the Greeter identifier,” which is the type of the constructor. This type contains all the static members and constructors of the class. Then, as before, use new on greeterMaker to create an instance of Greeter.
Use classes as interfaces
A class definition creates two things: the instance type of the class and a constructor. Because classes create types, you can use classes where interfaces are allowed.
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1.y: 2.z: 3};
Copy the code
function
- js
// Named function
function add(x, y) {
return x + y;
}
// Anonymous function
let myAdd = function(x, y) { return x + y; };
Copy the code
Function types
Add a type for a function
function add(x: number, y: number) :number {
return x + y;
}
let myAdd = function(x: number, y: number) :number { return x + y; };
Copy the code
Optional and default parameters
- Necessary parameters
function buildName(firstName: string, lastName: string) {
return firstName + "" + lastName;
}
let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob"."Adams"."Sr."); // error, too many parameters
let result3 = buildName("Bob"."Adams"); // ah, just right
Copy the code
- Optional parameters
function buildName(firstName: string, lastName? :string) {
if (lastName)
return firstName + "" + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // works correctly now
let result2 = buildName("Bob"."Adams"."Sr."); // error, too many parameters
let result3 = buildName("Bob"."Adams"); // ah, just right
Copy the code
- The default parameters
function buildName(firstName: string, lastName = "Smith") {
return firstName + "" + lastName;
}
let result1 = buildName("Bob"); // returns "Bob Smith"
let result2 = buildName("Bob".undefined); // returns "Bob Smith"
let result3 = buildName("Bob"."Adams"."Sr."); // error, too many parameters
let result4 = buildName("Bob"."Adams"); // returns "Bob Adams"
Copy the code
Arguments with default initialization after all required arguments are optional and, like optional arguments, can be omitted when calling a function. That is, the optional parameter shares the parameter type with the trailing default parameter.
The remaining parameters
Required, default, and optional parameters have one thing in common: they represent a parameter. Sometimes, you want to manipulate multiple arguments at once, or you don’t know how many arguments will be passed in. In JavaScript, you can use arguments to access all incoming arguments. In TypeScript, you can collect all arguments into a single variable:
function buildName(firstName: string. restOfName:string[]) {
return firstName + "" + restOfName.join("");
}
let employeeName = buildName("Joseph"."Samuel"."Lucas"."MacKinzie");
Copy the code
The ellipsis is also used on function type definitions with remaining arguments:
function buildName(firstName: string. restOfName:string[]) {
return firstName + "" + restOfName.join("");
}
let buildNameFun: (fname: string. rest:string[]) = > string = buildName;
Copy the code
this
let deck = {
suits: ["hearts"."spades"."clubs"."diamonds"].cards: Array(52),
createCardPicker: function() {
return function() {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13}; }}}let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
Copy the code
This refers to window and is undefined in strict mode
this
parameter
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () = > Card;
}
let deck: Deck = {
suits: ["hearts"."spades"."clubs"."diamonds"].cards: Array(52),
// NOTE: The function now explicitly specifies that its callee must be of type Deck
createCardPicker: function(this: Deck) {
return () = > {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13}; }}}let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
Copy the code
overloading
let suits = ["hearts"."spades"."clubs"."diamonds"];
function pickCard(x: {suit: string; card: number; } []) :number;
function pickCard(x: number) :{suit: string; card: number; };
function pickCard(x) :any {
// Check to see if we're working with an object/array
// if so, they gave us the deck and we'll pick the card
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13}; }}let myDeck = [{ suit: "diamonds".card: 2 }, { suit: "spades".card: 10 }, { suit: "hearts".card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
Copy the code
The generic<T>
function identity(arg: any) :any {
return arg;
}
Copy the code
Using any causes the function to accept arG arguments of any type, and some information is lost: the type passed in should be the same as the type returned. If we pass in a number, we just know that any type of value can be returned.
Therefore, we need a way to make the type of the return value the same as the type of the parameter passed in. Here, we use a type variable, which is a special variable that only represents a type, not a value.
function identity<T> (arg: T) :T {
return arg;
}
Copy the code
Once you define a generic function, you can use it in two ways.
- The first is to pass in all the arguments, including the type arguments:
let output = identity<string> ("myString");
Copy the code
This explicitly specifies that T is a string and is passed to the function as an argument, enclosed in <> instead of ().
- The second method is more common. We take advantage of type corollaries — that is, the compiler automatically helps us determine based on the parameters passed in
T
The type of:
let output = identity("myString");
Copy the code
A generic interface
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T> (arg: T) :T {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
Copy the code
You want to treat a generic parameter as a parameter to the entire interface, so you know exactly which generic type to use (e.g., Dictionary
instead of just Dictionary). This allows other members of the interface to know the type of the parameter.
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T> (arg: T) :T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
Copy the code
Generic constraint
function loggingIdentity<T> (arg: T) :T {
console.log(arg.length); // Error: T doesn't have .length
return arg;
}
Copy the code
Create an interface that contains the.length attribute and use this interface and the extends keyword to implement the constraint:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise> (arg: T) :T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
Copy the code
loggingIdentity(3); // Error, number doesn't have a .length property
loggingIdentity({length: 10.value: 3});
Copy the code
Module, the module
Usage can follow JS
- Export (multiple)
- Export Default (unique)
- import {data} from ‘./module’
- import {data as list} from ‘./module’
- import * as module from ‘./module’
To support CommonJS and AMD exports, TypeScript provides export = syntax.
To export a module using export =, you must import the module using TypeScript’s special syntax import Module = require(“module”).
let numberRegexp = / ^ [0-9] + $/;
class ZipCodeValidator {
isAcceptable(s: string) {
return s.length === 5&& numberRegexp.test(s); }}export = ZipCodeValidator;
Copy the code
import zip = require("./ZipCodeValidator");
Copy the code
A decorator @
A decorator is a special type of declaration that can be attached to a class declaration, method, accessor, property, or parameter. The decorator uses the form @expression. Expression must be evaluated as a function, which is called at run time with the decorated declaration information passed in as an argument.