Typescrit入门视频课<一>
- 一,typescript介绍:
- 二,错误检查+ts编译:
- 三,ts中的类型和新增的语法特性:
- 四,布尔值、数值、字符串的注解:
- 五,any和对象的注解:
- 六,null+undefined+void+object+never:
- 七,类型注解+类型推论+联合类型:
- 八,interface初识:
- 九,数组的注解:
- 十,函数的注解:
- 十一,类
- 11.1 类的注解:
- 11.2 类的修饰符:
- 11.3 readonly与参数属性
- 11.4 存取器:改变读取和赋值的行为
- 11.5 抽象类和静态属性
- 11.6 类高阶技巧
- 十二,接口:
- 12.1 接口+函数类型接口
- 12.2 可索引类型接口
- 12.3 类类型接口
- 12.4 类静态部分+实例部分
- 12.5 接口继承接口
- 12.6 接口的混合类型
- 12.7 接口继承类
- 十三,泛型:
- 13.1 泛型初识
- 13.2 泛型类型+泛型接口
- 13.3 泛型类+泛型约束+keyof操作符
- 13.4 多重泛型约束+交叉类型
- 13.5 泛型中的类类型
- 十四,元祖:
一,typescript介绍:
1,安装typescript:
windows: npm i typescripts -g
mac: sudo npm i typescripts -g
2,查看所有命令行:
tsc -h
二,错误检查+ts编译:
1,将ts编译为js:
tsc indes.ts
注:如果ts里写的是原生js,不用编译即可在浏览器中运行,如果ts中有ts语法,则必须编译后才能执行。ts还可在编写阶段检查语法错误。
2,js是动态类型,ts是静态类型:
js可以为变量重新赋值,而ts不行,会报错(类型一旦赋予,则不可更改)
三,ts中的类型和新增的语法特性:
(1)js数据类型:
原始数据类型:boolean string number null undefined symbol
引用数据类型:object function
(2)ts数据类型:
基础类型:boolean string number null undefined symbol any never
对象 interface
数组 number[ ] string[ ] boolean[ ] 泛型:Array
函数:eg:
let add = function ( a : number, b : number ) : number {
return a+b
}
add(1,2)
(3)新的语法特性:
as 断言
class : OOP面向对象的三大特性:封装、继承、多态
四,布尔值、数值、字符串的注解:
1,布尔值的注解:
let isDone:boolean = false
2,数值的注解:
let num:number = 123
注:配置ts自动编译为js:
(1)tsc – init : 生成tsconfig.json文件
(2)在tsconfig.json中:
‘rootDir’: ‘./src’
‘outDir’: ‘./dist’
(3)终端中运行tsc命令
将src中的ts编译为dist中的ts
3,字符串的注解:
let str:string = 'hhhh'
五,any和对象的注解:
1,any:代表任意数据类型。可重新赋值,且不具备语法提示。如果变量没有定义类型和赋值,初始类型为any
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
六,null+undefined+void+object+never:
1,void:没有任何数据类型,仅用在函数没有返回值的时候。在JS中,如果没有返回值,则默认返回undefined
2,null+undefined:
(1)null+undefined是所有类型的子类型
注:如果没有更改配置项,下列赋值将报错,null和undefined只能赋值给void和它们各自
let u: number = undefined;
方法一:更改tsconfig.json中的strictNullChecks为false
方法二:联合类型:
let num : number | undefined | null = undefined
3,never:永远不存在的值,主要针对报错和死循环
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
4,object:表示非原始类型:对象、数组、函数,也就是除number,string,boolean,symbol,null或undefined之外的类型。
(1)函数注解:
//普通函数
function test(a: number,b: number): string {
return ''+ a+ b
}
//declare:只声明函数,不关心函数的实现方式(花括号里的内容)
declear function create(obj:object | null ):void;
七,类型注解+类型推论+联合类型:
1,类型注解:
如果类型推断能推断出正确的类型,则不需要类型注解。
注:函数的参数,一般需要类型注解,否则有报错提示
2,联合类型:多个类型中选择任意一种类型
let a: string | number;
a = '123'
a = 123
(1)联合类型中,方法为类型共有方法,否则报错:
function test(a : number | string ) {
return a.split(',')
}
test(1);
//将报错,因为只有string有split方法
(2)在赋值的时候确认类型:
let a: number | string;
a = '123'
console.log(a.length) //无误
a = 10
console.log(a.length) //报错,a为数字,没有lenth
八,interface初识:
1,注解对象:
(1)默认:接口确定后,接口和对象里的属性不可多,也不可少,须一一对应。
interface Person{
name:string;
age:number;
}
let person:Person = {
name:'tom',
age:18
}
(2)可选属性:加问号可以让属性可选:
interface Person{
name:string;
age?:number;
}
let person:Person = {
name:'tom'
}
(3)任意属性:
固定格式:[propName: string]: any
interface Person{
name:string;
age?:number;
[propName: string]: any
}
let person:Person = {
name:'tom',
age:18,
sex:'male',
car:'mzd',
wife:10
}
(4)只读属性:
interface Person{
readonly id:number;
name:string;
age?:number;
[propName: string]: any
}
let person:Person = {
id:123,
name:'tom',
age:18,
sex:'male',
car:'mzd',
wife:10
}
person.id = 12345 //将报错,因为id属性只读
九,数组的注解:
1,注解方式:
(1)类型+[ ] :
let list: number[] = [1, 2, 3];
let list: (string | number)[] = [1,2 3,'1'];
(2)Array<类型>: let list: Array<number> = [1, 2, 3];
(3)接口方式:
interface List{
[index: number]: number | string
}
let list:List = [1,2,3,4,5,'5']
2,类数组:
function test () {
let args:IArguments = arguments;
//注:
interface IArguments{
[index:number]:any;
length:number;
callee:Function;
}
}
十,函数的注解:
1,注解方式:
//函数声明
function test(a: number, b: number){
return a+b //返回值为number
// console.log(a+b) ---> 返回值为void
// throw new Error() ---->返回值为Never
}
//函数表达式
let test1 = (a: number,b: number)=>number = function(a,b){
return a+b
}
2,可选参数:
注:可选参数放在必选参数后
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // works correctly now
3,默认参数:
可不写参数类型,因有类型推断。视为可选参数的子项
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
4,剩余参数:
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
5,解构赋值:
function test1(
{first, second}: {first: number, second: number} = {first = 1, second = 2}
){
return first+second
}
function test2({ first=2 }:{ first:number }){
return first
}
6,this指向:
注:如果你给编译器设置了–noImplicitThis标记。 它会指出 this.suits[pickedSuit]里的this的类型为any。
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};
}
// 修改:
// NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
//箭头函数中的this,默认绑定父函数中的this指向
}
}
let cardPicker = deck.createCardPicker(); // this指向deck对象
let pickedCard = cardPicker(); // 严格模式下,this指向undefined,非严格模式下,this指向window
alert("card: " + pickedCard.card + " of " + pickedCard.suit);
//***********************************//
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);
7,重载:
目的是表意清楚
function reverse(x:string):string;
function reverse(x:number):number;
function reverse(x: string | number) {
if(typeof x ==='string'){
return x.split('').reverse().join('')
}
if(typeof x ==='number'){
return Number(x.toString().split('').reverse().join(''))
}
}
十一,类
11.1 类的注解:
1,strictPropertyInitialization: false,否则会因没有为类成员设初始值而报错
2,注解方式:
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
3,super:
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); } //构造函数里的super,指父类的构造函数
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters); //构造函数外的super,指父类
}
}
let sam = new Snake("Sammy the Python");
sam.move();
11.2 类的修饰符:
1,面向对象的三大特性:封装、继承、多态
2,封装:类成员的修饰符
3,三种修饰符:
(1)public:
在TypeScript里,成员都默认为 public。自身可以调用、子类可以调用、实例可以调用
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
(2)private:只能自身调用
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // 错误: 'name' 是私有的.
(3)protected:
自身可以调用、子类可以调用
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 ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 错误
11.3 readonly与参数属性
1,readonly:只读,不可再写,不能修饰成员方法
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"; // 错误! name 是只读的.
2,参数属性:在param中使用修饰符
class Animal{
constructor(private name:string){ }
move(distanceInMeters: number){
console.log(`${this.name} moved ${distanceInMeters}m`);
}
}
11.4 存取器:改变读取和赋值的行为
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"; //赋值--->对应set函数
if (employee.fullName) {
//取值--->对应get函数
alert(employee.fullName);
}
11.5 抽象类和静态属性
1,静态属性:
我们也可以创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。 在这个例子里,我们使用 static定义 origin,因为它是所有网格都会用到的属性。 每个实例想要访问这个属性的时候,都要在 origin前面加上类名。 如同在实例属性上使用 this.前缀来访问属性一样,这里我们使用 Grid.来访问静态属性。
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}));
2,抽象类:
(1)作用:能够提供其他类的基类。特点:无法创建实例;在派生类中实现(抽象方法一定在实例中有实现)
(2)用法:
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
new Animal(); //错误
class Snake extends Animal {
makeSound(): void {
console.log('ssssss')
}
move(): void {
console.log('roaming the earch...');
}
}
11.6 类高阶技巧
1,定义类的时候,定义了一个类型
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());
2,定义类的时候,定义了一个构造函数
let Greeter = (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
})();
let greeter;
greeter = new Greeter("world");
console.log(greeter.greet());
3,把类当作接口使用
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
十二,接口:
12.1 接口+函数类型接口
1,接口:见第八讲
2,函数类型接口:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
}
12.2 可索引类型接口
1,含义:可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。(相当于为数组设置接口)
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
2,TypeScript支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
// 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
interface NotOkay {
[x: number]: Animal;
[x: string]: Dog;
}
//应为:
interface NotOkay {
[x: string]: Animal;
[x: number]: Dog; //number类索引签名会转换为字符串类索引签名。派生类应比父类包含范围更广
}
3,确保所有属性与其返回值类型相匹配:
interface NumberDictionary {
[index: string]: number;
length: number; // 可以,length是number类型
name: string // 错误,`name`的类型与索引类型返回值的类型不匹配,应为number
}
4,将索引签名设置为只读,这样就防止了给索引赋值
interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!
12.3 类类型接口
1,含义:对类的一部分行为的抽象。类实现接口中所有的属性和方法,抽象类中抽象方法需要实现
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number) { }
}
2,实现多个接口:
interface Alarm {
alert():void;
}
interface Light {
color:string;
lightOn():void;
lightOff():void;
}
class Clock implements Alarm, Light {
color='red';
alert(){
console.log('alert')
}
lightOn(){
console.log('lightOn')
}
lightOff(){
console.log('lightOff')
}
}
12.4 类静态部分+实例部分
1,constructor存在于类的静态部分,所以不在检查的范围内。
interface ClockConstructor {
new (hour: number, minute: number);
}
class Clock implements ClockConstructor {
currentTime: Date;
constructor(h: number, m: number) { } //报错
}
2,为构造函数定义接口:
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);
12.5 接口继承接口
和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
12.6 接口的混合类型
1,含义:一个对象可以同时做为函数和对象使用,并带有额外的属性(函数类型的interface,可以通过添加属性的方式来实现对象的interface)
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;
12.7 接口继承类
1,类可以实现接口;接口可以继承接口;接口可以继承类
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() { }
}
class TextBox extends Control {
select() { }
}
十三,泛型:
13.1 泛型初识
1,类型变量:一种特殊的变量,只用于表示类型而不是值。
function identity<T>(arg: T): T {
return arg;
}
我们给identity添加了类型变量T。 T帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了 T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。
我们定义了泛型函数后,可以用两种方法使用。
(1)第一种是,传入所有的参数,包含类型参数:
let output = identity<string>("myString"); // type of output will be 'string'
(2)利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型:
let output = identity("myString"); // type of output will be 'string'
注意我们没必要使用尖括号(<>)来明确地传入类型;编译器可以查看myString的值,然后把T设置为它的类型。 类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的情况下,这是可能出现的。
13.2 泛型类型+泛型接口
1,泛型类型:
(1)写法:
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
(2)用对象字面量来定义泛型函数:
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: {<T>(arg: T): T} = identity;
(3)对象字面量拿出来做为一个接口:
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
(4)当我们使用 GenericIdentityFn的时候,还得传入一个类型参数来指定泛型类型(这里是:number),锁定了之后代码里使用的类型。
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
13.3 泛型类+泛型约束+keyof操作符
1,泛型类定义方式:
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; };
2,类型约束:
(1)定义一个接口来描述约束条件。 创建一个包含 .length属性的接口,使用这个接口和extends关键字来实现约束
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;
}
(2)继承类型:
type Lengthwise = string;
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;
}
3,在泛型约束中使用类型参数:
type k = keyof { a: 1, b: 2, c: 3, d: 4 }; //对象的键值
function getProperty<T,K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a"); // okay
getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
13.4 多重泛型约束+交叉类型
1,交叉类型:
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
class Person {
constructor(public name: string) { }
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
13.5 泛型中的类类型
function create<T>(c: {new(): T; }): T {
return new c();
}
class BeeKeeper {
hasMask: boolean;
}
class ZooKeeper {
nametag: string;
}
class Animal {
numLegs: number;
}
class Bee extends Animal {
keeper: BeeKeeper;
}
class Lion extends Animal {
keeper: ZooKeeper;
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag; // typechecks!
createInstance(Bee).keeper.hasMask; // typechecks!
十四,元祖:
1,含义:元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
// Declare a tuple type
let x: [string, number]; //确定长度和类型,但push和扩展运算符可以改变长度
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
2,当push越界时,类型为联合类型:
// Declare a tuple type
let x: [string, number]; //确定长度和类型
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
x.push('hhh') //right
x.push(123) //right
x.push(false) //wrong,不符合string | number
3,元祖元素可选:加问号
let list:[number, string, boolean?];
list = [123, '123' ]
4,扩展运算符:
declear funtion test(...args:[number, string, boolean]):void; //用declear可以不用定义函数的实现
declear funtion test(arg1:number,arg2:string, arg3:boolean):void;
let list : [number, ...string[]] = [1, '1', '2', '3'] //限制类型,而不限制长度