TS学习笔记

JS:易学易用,开发过程容易留下隐患,后期维护困难。不适合开发大型项目

  JS中的变量是一个动态类型

TS:微软开发

解决JS存在的缺点,以JS为基础构建的语言,是JS的一个超集,在JS的基础上增加了一些功能

  给变量赋予了一个类型,变为静态类型的语言,可以在任何支持JS的平台中执行。

  TS不能被JS解析器执行,需要编译为JS才能执行。

  提高代码的可维护性

TS增加了什么??

  类型、支持ES的新特性、添加了ES不具备的新特性、丰富的配置选项、强大的开发工具

编译:tsc 文件名

 类型的注解

  类型注解是TS中记录函数或变量约束的简便方法

1 functiongreeter(person: string){
2 return"Hello, " + person;
3 }
4 var user = [0, 1 , 2];
5 document.body.innerHTML = greeter(user);

重新编译,将会报错

接口

接口:描述具有相同属性的对象。

例如:使用一个接口,描述了具有firstName和lastName字段的对象。

复制代码
1 interface Person{
2     firstName:string;
3     lastName:string
4 }
5 function greeter(person:Person){
6    return "HELLO"+person.firstName + " "+person.lastName         
7 }
8 var user = {firstName:"NIE",lastName:"Lifang"};
9 document.body.innnerHtml = greeter(user);
复制代码

类和接口的良好配合使用,决定一个程序员的抽象水平。

复制代码
 1 class Student{
 2   fullName:string;
 3   constructor(public firstName,public middleInitial,public lastName){
 4      this.fullName = firstName+" "+middleInitial + " "+lastName
 5     }        
 6 }
 7 interface Person{
 8   firstName:string;
 9   lastName:string;    
10 }
11 function greeter(person:Person){
12    return "HELLO" + person.firstName + " " + person.lastName;  
13 }
14 var user = new Student("Nie",'Li',"Fang");
15 document.body.innerHTML = greeter(user);
复制代码

基础数据类型

 

let a:10;  // 限制a的值只能是10
// a=11   报错
let b:"male"|"female"; //b只能取male或者female

let c:boolean | string;

数组:两种方式定义数组

let list:number[] = [1,2,3]
let list:Array<number> = [1,2,3]

元组Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素类型不必相同。

let x:[string,number];
x = ['hello',10]

枚举enum

enum Color {Red,Green,Blue}'
let c:Color = Color.Green;

默认情况下,从0开始编号,可以手动指定成员的数值。

enum Color{Red=1,Green,Blue};
let c:Color = Color.Green;

任意值 any

表示任意类型,相当于对该变量关闭了TS的类型检测。TS时,不建议使用any类型

声明变量如果不指定类型,则TS解析器会自动判断类型为any,它可以赋值给任意变量

let d:any
let s:string;
//d的类型是any,他可以赋值给任意变量
s = d;

有时候,我们在开发阶段还不清楚类型的变量,那我们可以使用any类型来标记这些变量

let notSure:any = 4;
notSure = 'may be a string instead';
notSure = false;

Object类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法

let prettySure:Object = 4;
prettySure.toFixed; //Error

当我们只知道部分数据的类型时,any也是有用的。比如一个数组,它包含了不同类型的数据

let list:any[] = [1,true,'free'];
list[1] = 100;

unknown

实际就是一个类型安全的any

//unknown表示未知类型的值;
let e:unknown
e = 10;
e="hello";
let s:string;
s = e;  // 报错

any 和 unknown的区别:unknown不能赋值给任意类型的变量,但是any可以赋值给任意类型的变量

进行类型判断后,unknown才可以赋值给其他变量

if(typeof e ==="string"){
    s = e
}

空值void

void类型与any正好相反,它表示没有任何类型,当一个函数没有返回值,或者返回值是undefined或null时,通常会见到其返回值类型是void

function warnUse():viod{
  alert("This is my waring message")      
}

声明一个void类型的变量没有什么大用,此变量只能给他赋予undefined和null

let unusable:viod = undefined

Never

永远不会返回结果

  never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。

  never类型是任何类型的子类型,也可以赋值给任何类型

复制代码
 1 // 返回never的函数必须存在无法达到的终点
 2 function error(message: string): never {
 3     throw new Error(message);
 4 }
 5 
 6 // 推断的返回值类型为never
 7 function fail() {
 8     return error("Something failed");
 9 }
10 
11 // 返回never的函数必须存在无法达到的终点
12 function infiniteLoop(): never {
13     while (true) {
14     }
15 }
复制代码

类型断言

告诉解析器变量的实际类型

使用类型断言,可以将unknown的值赋值给其他变量

let s:string;
let e:unknown
e = 10;
e="hello";
// 类型断言
s = e as string;
// OR
s = <string> e

有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。

通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。

类型断言有两种形式。 其一是“尖括号”语法:

let someValue:any = "this is a string";
let strLength:number = (<string>someValue).length;

另外一种写法是as

let someValue:any = "this is a string";
let strLength:number = (someValue as string).length;

接口

接口初探

function printLabel(labeledObj:{label:string}){
  console.log(labeledObj.label)
}
let myObj = {size:10,label:'Size 10 Object'};
printLabel(myObj)

使用接口重写上面的例子

interface labeledValue{
    label:string;
}
function printLabel(labeledObj:labeledValue){
  console.log(labeledObj.label);
}
let myObj = {size:10,label:'Size 10 Object'};
printLabel(myObj);

LabelledValue接口就好比一个名字,用来描述上面例子里的要求。 它代表了有一个 label属性且类型为string的对象。

可选属性

接口里的属性不全是必需的,有些只是某些条件下存在,即给函数传入的参数对象只有部分属性赋值了。

复制代码
 1 interface SquareConfig{
 2   color?:string;
 3   width?:number;
 4 }
 5 function createSquare(config:SquareConfig):{color:string;area:number}{
 6   let newSquare = {color:"white",area:100};
 7   if(config.color){
 8     newSquare.color = config.color
 9   }
10   if(config.width){
11     newSquare.area = config.width * config.width;
12   }
13   return newSquare;
14 }
15 let mySquare = createSquare({color:'red'})
复制代码

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。

可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。

只读属性

一些对象属性只能在对象刚刚创建的时候修改其值

interface Point{
  readonly x:number;
  readonly y:number;
}
let p1:Point = {x:10,y:10}
p1.x = 5; //error
let a:number[] = [1,2,3,4];
let ro:ReadonlyArray<number> = a;
ro.push(5); //error
a = ro;//error

代码最后一行,就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。但是可以用类型断言来重写:

a = ro as number[];

readonly VS const

最简单的判断该用readonly还是const的方法是看要把它作为变量使用还是作为一个属性。作为变量的话用const,若作为属性则用readonly。

额外的属性检查

复制代码
interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
    // ...
}

let mySquare = createSquare({ colour: "red", width: 100 });
复制代码

在上述例子中,createSquare的color拼写错误,这时候将会失败。绕开这些检查,最简单的方法就是使用类型断言。

let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
//area: 100
color: "white"

然而,最佳的方式是能够添加一个字符串索引标签,做为特殊用途使用的额外属性。例如 带有上面定义的color和width属性,并且还会带有任意数量的其他属性,那我们可以这样定义它。

interface SquareConfig{
  color?:string;
  width?:number;
  [propName:string]:any;      
}

函数类型

除了描述带有属性的普通对象外,接口还可以描述函数的类型。它就像一个只有参数列表和返回值类型的函数定义。

interface SearchFunc{
  (source:string,substring:string):boolean;  
}

下例展示了如何创建一个函数类型的变量

复制代码
let mySearch:SearchFunc;
mySearch = function(source:string,substring:string){
  let res = source.search(substring);
  if(res == -1){
      return false;      
    }    
    else{
       return true;
    }  
}
复制代码

对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。比如我们重写上面的例子。

复制代码
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
  let result = src.search(sub);
  if (result == -1) {
    return false;
  }
  else {
    return true;
  }
}
复制代码

可索引类型

描述那些可以通过索引得到的类型。可索引类型具有一个 索引签名,描述了对象索引的类型,还有相应的索引返回值类型。

interface StringArray{
  [index:number]:string;
}
let myArray:StringArray;
myArray = ["Bob","Fred"];
let myStr:string = myArray[0]

字符串索引签名能够很好的描述dictionary模式,并且它们也会确保所有属性与其返回值类型相匹配。

interface NumberDictionary{
  [index:string]:number;
  length:number;
  name:string;//错误,'name'的类型不是索引类型的子类型      
}

还可以将索引签名设置为只读,这样就防止了给索引赋值。

interface ReadonlyStringArray{
  readonly[index:number]:string;  
}
let myArray:ReadonlyStringArray = ["Alice","Bob"];
myArray[2] = "Jane";//error!不能设置myArray[2],因为索引签名是只读的。

扩展接口

能够从一个接口里复制成员到另外一个接口,可以更灵活的将接口分割到可重用的模块。

复制代码
interface Shape{
  color:string;  
}
interface Square extends Shape{
  sideLength:number;
}
let square = <Square>{};
square.color = 'blue';
square.sideLength = 10;
复制代码

一个接口可以继承多个接口,创建出多个接口的合成接口。

复制代码
 1 interface Shape{
 2   color:string;
 3 }
 4 interface PenStrokr{
 5  penWidth:number;
 6 }
 7 interface Square extends Shape,PenStroke{
 8  sideLength:number;
 9 }
10 let square = <Square>{};
11 square.color = 'blue';
12 square.sideLength = 10;
13 square.penWidth = 5.0;
复制代码

混合类型

某个接口能够同时具有多种类型,可以同时作为函数和对象使用,并带有额外的属性。

复制代码
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;
复制代码

接口继承类

当接口继承了一个类时。它会继承类的成员但不包括其实现。接口同样会继承类的private和protected成员。

当你有一个很深层次的继承,但是只想代码针对拥有特定属性的子类起作用时。

class Control{
  private state:any
}
interface SelectableControl extends Control{
  select():void;
}

例子:

复制代码
class Greeter{
  greeting:string;
  constructor(message:string){
    this.greeting = message;
 }
 greet(){
   return "HELLO" + this.greeting;
 }
}
let greeter = new Greeter("World");
复制代码

上述这个类具有3个成员,一个是greeting的属性,一个是构造函数,一个是greet方法。

我们在引用任何一个类成员的时候都用了this,表示我们访问的是类的成员。

继承

使用继承来扩展现有的类

复制代码
 1 class Animal{
 2   name:string;
 3   constructor(thename:string){
 4     this.name = thename;
 5   }
 6   move(distanceMeters:number=0){
 7     console.log(`${this.name} moved ${distanceMeters}m`);
 8   }
 9 }
10 class Snake extends Animal{
11   constructor(name:string){
12     super(name);
13   }
14   move(distanceMeters=5){
15     console.log("Slithering...");
16     super.move(distanceMeters);
17   }
18 }
19 let sam = new Snake("Sammy the Python");
20 sam.move();//Slithering... Sammy the Python moved 5m.
复制代码

Snake类是基类Animal的子类,可以访问其属性和方法。

包含构造函数的派生类必须调用super(),它会执行基类的构造方法。

这个例子演示了如何在子类里重写父类的方法,Snake类创建了move方法,重写了Animal继承来的move方法,使得move方法根据不同的类可以具有不同的功能。

公共,私有与受保护的修饰符

默认为public,我们可以自由的访问程序里定义的成员。

private

当成员被标记为private,就不能在声明它的类的外部访问

class Animal{
  private name:string;
  constructor(theName:string){
    this.name = theName;
  }
}
new Animal("Cat").name; //Error:'name' is private

当我们比较两种不同的类型时,并不在乎它们从何处而来,如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。

然而,当我们比较带有privateprotected成员的类型的时候,情况就不同了。 如果其中一个类型里包含一个 private成员,那么只有当另外一个类型中也存在这样一个private成员, 并且它们都是来自同一处声明时,我们才认为这两个类型是兼容的。 对于 protected成员也使用这个规则。

复制代码
class Animal{
 private name:string;
 constructor(thName:string){
   this.name = thName;
 }
}
class Rhino extends Animal{
 constructor(){
   super("Rhino");
 }
}
class Employee{
 private name:string;
 constructor(thName:string){
   this.name = thName;
 }
}
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino;
animal = employee; //Error:Animal and Employee are not compatible
复制代码

因为 AnimalRhino共享了来自Animal里的私有成员定义private name: string,因此它们是兼容的。 然而 Employee却不是这样。当把Employee赋值给Animal的时候,得到一个错误,说它们的类型不兼容。 尽管 Employee里也有一个私有成员name,但它明显不是Animal里面定义的那个。

protected

protected修饰符与private修饰符很相似,但是protected成员在派生类中仍然可以访问

复制代码
class Person{
  protected name:string;
  construtor(name:string){
    this.name = name;
  }
}
class Employee extends Person{
  private department:string;
  construtor(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);//error
复制代码

PS:我们不能在Person类外去使用name,但是我们仍可以通过Employee 类的实例方法访问,因为Employee是由Person派生而来的。

构造函数也可以被标记为protected,这意味着这个类不能在包含他的类外被实例化

复制代码
class Person{
  protected name:string;
  protected construtor(name:string){
    this.name = name;
  }
}
class Employee extends Person{
  private department:string;
  construtor(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");
let john = new Person("John"); //error:The "Person" construtor is protected
复制代码

readonly修饰符

可以使用readonly关键字将属性设置为只读,只读属性必须在声明时或构造函数里被初始化。

复制代码
class Octopus{
  readonly name:string;
  readonly numberOfLegs:number=8;
  constuctor(thName:string){
    this.name = thName;
  }
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; //error!name is readonly
复制代码

参数属性

在上面的例子中,我们不得定义一个受保护的成员name和一个构造函数参数thName在Person类里,并且立刻给name和thName赋值。

参数属性可以很方便的让我们在一个地方定义并初始化一个成员。下面是对Animal类的修改版,使用了参数属性。

class Animal{
  constrctor(private name:string){}
  move(distanceInMeters:number){
    console.log(`${this.name} moved ${distanceInMeters}m`);
  }
}

存取器

我们先检查用户密码是否正确,然后再允许其修改员工信息。

复制代码
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:Unautherizes")
    }
  }
}
let employee = new Employee();
employee.fullName = 'Bob Smith';
if(employee.fullName){
  alert(employee.fullName)
}
复制代码

存取器要求你将编译器设置为输出ECMAScript5或更高,其次,只带有 get 不带有 set 的存取器自动被推断为readonly。

静态属性

类的实例成员:仅当类被实例化的时候才被初始化的属性。

类的静态成员:这些属性存在于类本身上面而不是类的实例上。

  每个实例想要访问这个属性的时候,都需要在前面加上类名如同实例属性上使用this.前缀访问属性一样。

复制代码
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;
  }
  construtor(public scale:number){}
}
let grid1 = new Grid(1.0);
console.log(grid1.calculateDistanceFromOrigin({x:10,y:10}))
复制代码

抽象类

抽象类作为其他派生类的基类使用,他们一般不会直接被实例化。不同于接口,抽象类可以包含成员的实现细节。abstract 关键字用于定义抽象类和在抽象类内部定义抽象方法。

复制代码
 1 abstract class  Department {
 2   constructor(public name:string) {
 3   }
 4   printName():void{
 5     console.log("department name:"+this.name)
 6   }
 7   abstract printMeeting():void
 8 }
 9 class AccountingDepartment extends Department {
10   constructor() {
11     super("Accounting and Auditing");
12   }
13   printMeeting():void{
14     console.log('The Accounting Department meets each Monday at 10am.');
15   }
16   generateReport():void{
17     console.log("Generating accounting reports...");
18   }
19 }
20 let department:Department;
21 department = new Department(); //error: cannot create an instance of an abstract class
22 department = new AccountingDepartment();
23 department.printName();
24 department.printMeeting();
25 department.generateReport(); //error: method doesn't exist on declared abstract type
复制代码

函数

函数

//Named function
function add(x,y){
return x+y;
}
//anonymous function
let myAdd = function(x,y){
  return x+y;
}

 函数类型

为函数定义类型

  为上面函数定义类型

function add(x:number,y:number):number{
  return x+y;
}
let myAdd = function(x:number,y:number):number{return x+y};

书写完整类型

let myAdd:(x:number,y:number)=>number = 
    function(x:number,y:number):number{return x+y};

函数类型包含两个部分:参数类型和返回值类型。当写出完整函数类型的时候,这两部分都是需要的。

  我们以参数列表的形式写出参数类型,为每个参数指定一个名字和类型。这个名字只是为了增加可读性,我们也可以这样写。

let myAdd:(baseValue:number,increment:number)=>number = 
    function(x:number,y:number):number{return x+y};

只要参数类型是匹配的,那么就认为它是有校的函数类型,而不在乎参数名是否正确。

第二部分是返回值类型,对于返回值,我们在函数和返回值类型之前使用  => 符号,使之清晰明了。返回值类型是函数类型的必要部分,如果函数没有返回任何值,那么必须指定返回值类型为 void 而不能留空。

推断类型

在赋值语句的一边指定了类型但是另外一边没有类型对话,TS编译器会自动识别出类型:

let myAdd = function(x:number,y:number):number{ return x+y };

let myAdd:(baseValue:number,increment:number)=>number = 
    function(x,y) {return x+y };

可选参数和默认参数

TS里面的每个函数参数都是必须的。传递给一个函数的参数个数必须与函数期望的参数个数一致。

JS里面,每个参数都是可选的,可传可不传,没传参的时候,他的值就是undefined。

在TS里面,我们可以在参数名旁使用 ? 实现可选参数的功能。

复制代码
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
复制代码

可选参数必须跟在必须参数的后面。

在TS里面,我们也可以为参数提供一个默认值,当用户没有传递这个参数或者传递的值是undefined时。

function buildName(firstName:string,lastName="Smith"){
    return firstName + " " + lastName;
}
let result1 = buildName("Bob");                  // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined);       // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result4 = buildName("Bob", "Adams");         // ah, just right

可选参数与末尾的默认参数共享参数类型。

与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。如果带默认值的参数出现在必须参数的前面,用户必须明确传入 undefined 来获得默认值。

function buildName(firstName = "Will",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");         // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams");     // okay and returns "Will Adams"

剩余参数

有时,你想同时操作多个参数,或者不知道会有多少个参数传递进来。在JS里面,可以使用arguments来访问所有传入的参数。

在TS里面,可以把所有参数收集到一个变量里:

function buildName(firstName:string,...restOfName:string[]){
  return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph","Samuel","Lucas")

剩余参数会被当做个数不限的可选参数,可以一个都没有,同样也可以有任意个。

this和箭头函数

 

重载

JS本身是个动态语言,JS里函数根据传入参数的不同而返回不同类型的数据,方法是同一个函数提供多个函数类型定义来进行函数重载。

下面是一个重载pickCard函数的例子

复制代码
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);
复制代码

泛型

“泛型”来创建可重用的组件,一个组件可以支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件。

比如:创建一个函数,返回任何传入他的值

如果使用any来定义函数:

function identity(arg:any):any{
  return arg
}

使用any已经能接收任何类型的arg参数,但是任何类型的值都可能被返回。丢失了:传入的类型应该和返回的类型保持一致。

因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的,这里我们使用了 类型变量。

function identity<T>(arg:T):T{
  return arg
}

这里我们给identity添加了类型变量T,T帮助我们捕获用户传入的类型,之后我们就可以使用这个类型。之后我们再次使用了T当作返回值类型,我们就可以知道参数类型和返回值类型是一致的了。

我们定义了泛型函数之后,可以用两种方法使用它。

let output = identity<string>("myString")

这里我们明确指定了T是string类型,作为一个参数传给了函数,使用<>而不是()。

第二种方法是利用类型推断

let output = identity("myString")

这里我们没必要使用<>来明确传入类型。编译器可以查看myString的值,然后把T设置为他的类型。

如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的情况下,这是可能出现的。

泛型变量

假设我们现在想操作T类型的数组而不直接是T,我们可以像创建其他数组一样创建它。

function loggingIdentity<T>(arg:T[]):T[]{
  console.log(arg.length);
  return arg
}

泛型接口

泛型类

泛型类使用<>括起泛型类型,跟在类名后面

复制代码
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;
}
myGenericNumber.add(1,2)
复制代码

与接口一样,直接把泛型类型放在类后面,可以帮助我们确认所有的属性都在使用相同的类型。

类有两部分:实例部分和静态部分。泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。

枚举

一个枚举类型可以包含零个或者多个枚举成员。枚举成员具有一个数字值,它可以是 常数 或者计算得出的值。当满足以下条件时,枚举成员被当作是常数:

1、不具有初始化函数并且之前的枚举成员是常数。在这种情况下,当前枚举成员的值为上一个成员的值加1.但第一个枚举元素是个例外。如果它没有初始化方法,那么他的初始值为0。

2、枚举成员常用 常数枚举表达式 初始化。常数枚举在enum关键字前使用const修饰符。

复制代码
const enum Directions{
  Up,
  Down,
  Left,
  Right  
}
let directions = [Directions.Up,Directions.Down,Directions.Left,Directions.Right];
// 生成后的代码为:
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
复制代码

 配置TS:tsconfig.json

复制代码
{
    /*
    是ts编译器的配置文件,ts一起可以根据他的信息来对代码进行编译
    include:用来指定哪些ts文件需要被编译
    路径:**表示任意目录
          *表示任意文件
    exclude:不需要被编译的文件目录
        默认值:["node_modules","bower_components","jspm_packages"]
    extends:继承配置文件
    files:指定被编译文件的列表,ts文件少时才会用到
    */
    "include": [
        "./src/**/*"
    ],
    // "exclude": [
    //     "./src/hello/**/*"
    // ],

    /*
    compilerOptions:编译器的选项
    */
    "compilerOptions": {
        // target 指定ts被编译为js的版本,默认时es3
        "target": "ES6",
        // module 模块化,指定要使用的模块化的规范,可选值有:
        // 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'esnext'
        "module": "system",
        // lib:指定项目中要使用的库
        "lib": ["dom","ES6"],
        // outDir:指定编译后文件所在的目录
        "outDir": "./dist",
        // 将代码合并为一个文件,所有的全局作用域中的代码合并到同一个文件(不常用)
        // "outFile": "./dist/app.js",
        // allowJs:是否对js文件进行编译,默认是false
        "allowJs": false,
        // checkJs检查js代码是否符合语法规范,默认是false
        "checkJs": false,
        // removeComments:编译时是否移除注释
        "removeComments": true,
        // noEmit:不生成编译后的文件,默认是false
        "noEmit": false,
        // noEmitOnError:当有错误时不生成编译文件
        "noEmitOnError": true,
        // alwaysStrict:用来设置编译后的文件是否使用严格模式,默认是false
        "alwaysStrict": true,
        // noImplicitAny:不允许使用隐式的any类型
        "noImplicitAny": true,
        // noImplicitThis:不允许不明确类型的this
        "noImplicitThis": true,
        // strictNullChecks:严格的检查空值null
        "strictNullChecks": false,
        // strict:所有严格检查的总开关,true时(alwaysStrict+noImplicitAny+noImplicitThis+strictNullChecks)都会变为true
        "strict": true
    }
}
复制代码

 

  

posted @   聂丽芳  阅读(303)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示