标签搜索

TypeScript学习笔记

Augenstern
2025-10-17 / 0 评论 / 5 阅读 / 正在检测是否收录...

TypeScript入门笔记

教程地址:
官方文档:TypeScript
菜鸟教程:TypeScript入门教程

简单介绍

1、TypeScript由微软公司开发,是一种开源的编程语言
2、TypeScript是JavaScript的超集,拓展了JavaScript的语法,因此,TS也是兼容JS代码的,只会对TS代码进行编译

安装

有两种主要的方式来获取TypeScript工具:

  • 通过npm(Node.js包管理器)
  • 安装Visual Studio的TypeScript插件

这里采用npm的方式进行安装

npm install -g typescript

查看版本

tsc -V      

初始化一个项目

tsc --init  

基础语法

数据类型
  • 布尔值
let isDone: boolean = false;
  • 数字
let decLiteral: number = 6;              //十进制
let hexLiteral: number = 0xf00d;         //十六进制
let binaryLiteral: number = 0b1010;      //二进制
let octalLiteral: number = 0o744;        //八进制
  • 字符串
let name: string = "bob";
name = "smith";

字符串除用"'表示外,还可以使用模板字符串,可以定义多行文本和内嵌表达式,字符串由反引 号`包围,并且以${ expr }这种形式嵌入表达式

let name: string = `Xiaohu`;
let age: number = 37;
let sentence: string = `Hello, my name is ${name}. I'll be ${age+1} years old next     month;`
console.log(sentence);
//显示结果
Hello, my name is Xiaohu. I'll be 38 years old next month;
  • Symbol
    Symbol 是 唯一且不可变 的数据类型,通常用作对象属性的键
// Symbol 类型注解
const sym1: symbol = Symbol();
const sym2: unique symbol = Symbol("key"); // unique symbol 类型

// 作为对象属性
const obj = {
  [sym2]: "123"  // 使用 unique symbol
};

console.log(obj[sym2]); // "123"
  • 数组
    两种定义方式
    第一种在元素类型后面添加[]
let list: number[] = [1, 2, 3];

第二种使用数组放心,Array<元素类型>

let list: Array<number>
  • 元组
    元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
let x: [string, number];
x = ['hello', 10]; //  正确
x = [10, 'hello']; //  错误

当访问一个越界的元素,会使用 联合类型 替代

x[3] = 'world';
x[4] = 100;
console.log(x[5].toString()); 
//以上都是可以的, 字符串和数字都属于(string | number)类型,'string' 和 'number' 都有 toString
x[6] = true; // Error, 布尔不是(string | number)类型
  • 联合类型
    一个变量是多个数据类型中的某一个,可以使用联合类型, 的关系
function getArg(param: string | number) {
  return param.length
}
  • 交叉类型
    一个变量是多个数据类型的总和,可以使用交叉类型, 的关系
interface Person {
  name: string;
  age: number;
}

interface Employee {
  company: string;
  employeeId: number;
}

// BusinessPerson 必须同时拥有 Person 和 Employee 的所有属性
type BusinessPerson = Person & Employee;
  • 枚举
    enum类型是对JavaScript标准数据类型的一个补充,使用枚举类型可以为一组数值赋予友好的名字
enum Color {Red,Green,Blue}
let c:Color = Color.Green;
console.log(c); //显示1

默认情况下,从0开始为元素编号,也可以手动的指定成员的数值;枚举类型也可以通过值找到对应名字

enum Color {Red=1,Green=2,Blue=4} //Green和Blue 不赋值,则默认从2开始
let colorName:string = Color[4];
console.log(colorName); // 显示Blue
  • Any
    Any表示任意类型,但不同在于Object只允许赋值,而不能调用其中的方法,即使对象内部真的有对应的方法
let notSure: any = 4;
notSure.ifItExists(); // 正常
notSure.toFixed(); // 正常

let prettySure: Object = 4;
prettySure.toFixed(); // 报错,提示Object没有对应方法
  • Void
    表示没有任何类型,比如在函数没有返回值时,返回void
function warnUser(): void {
  console.log("This is my warning message");
}

void 只能赋undefined和null

let unusable: void = undefined;
  • Null 和 Undefined
    TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null,是所有类型的子类型(默认情况);--strictNullChecks标记,null和undefined只能赋值给void和它们各自
  • Never
    never类型表示的是那些永不存在的值的类型,never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外), 即使 any也不可以赋值给never。
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
  return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
  while (true) {
  }
}
  • Object
    object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型
接口

TypeScript的核心原则之一是对值所具有的shape进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约

普通接口
interface LabelledValue{
  label:string;
}

function printLabel(labelledObj:LabelledValue){
  console.log(labelledObj.label);
}

let myObj = {size:10,label:"Size 10 Object"};
printLabel(myObj);

LabelledValue接口就好比一个名字,用来描述上面例子里的要求。它代表了有一个label属性且类型为string的对象。需要注意的是,我们在这里并不能像在其它语言里一样,说传给printLabel的对象实现了这个接口。我们只会去关注值的外形。 只要传入的对象满足上面提到的必要条件,那么它就是被允许的。

还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。

可选属性

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号,可选属性的好处之一是 可以对可能存在的属性进行预定义 ,好处之二是 可以捕获引用了不存在的属性时的错误

interface SquareConfig {
 color?: string;
 width?: number;
}
只读属性

一些对象属性只能在对象刚刚创建的时候修改其值,可以在属性名前用readonly来指定只读属性
属性用readonly,变量用const

interface Point {
   readonly x: number;
   readonly y: number;
}
}
函数类型

接口也可以描述函数类型

interface SearchFunc{
  (source:string,subString:string):boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
  let result = source.search(subString);
  if (result == -1) {
    return false;
  }
  else {
    return true;
  }
}
//对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配
mySearch = function(src: string, sub: string): boolean {
  let result = src.search(sub);
  if (result == -1) {
    return false;
  }
  else {
    return true;
  }
}
数组类型

数组类型具有一个index类型表示索引的类型,还有一个相应的返回值类型表示通过索引得到的元素的类型

interface StringArray {
  [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];
interface NumberDictionary {
  [index: string]: number;
  length: number;    // 可以,length是number类型
  name: string       // 错误,`name`的类型不是索引类型的子类型
}
实现接口

与Java中的接口一致,TypeScript也能够用它来明确的强制一个类去符合某种契约

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}

class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) 
}
类静态部分与实例部分

实例部分 :当你使用 new 创建一个类的实例时,实例上拥有的属性和方法。

静态部分 :类本身(构造函数)上拥有的属性和方法。

当你用一个接口去约束一个类时(使用 implements 关键字),这个接口 只会检查类的实例部分,而不会检查静态部分

当你用 构造器签名 (用来描述构造函数类型的一种特殊语法)去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误,因为当一个类实现了一个接口时,只对其实例部分进行类型检查。 constructor存在于类的静态部分,所以不在检查的范围内。

interface ClockConstructor {
    new (hour: number, minute: number);
}

class Clock implements ClockConstructor {
    currentTime: Date;
    constructor(h: number, m: number) 
}

因此,我们应该直接操作类的静态部分。 看下面的例子,我们定义了两个接口, ClockConstructor为构造函数所用和ClockInterface为实例方法所用。 为了方便我们定义一个构造函数 createClock,它用传入的类型创建实例。

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);

因为createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 7, 32)里,会检查AnalogClock是否符合构造函数签名

继承接口

接口可以相互继承,也可以继承多个接口,创建出多个接口的合成接口

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;
接口继承类

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。当创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() 
}

class TextBox extends Control {
    select() 
}

// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl {
    select() 
}

class Location {

}

与Java中的类很类似,成员有属性、构造方法、方法

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("world");
继承

派生类通常被称作子类 ,基类通常被称作 超类

class Animal {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}

class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}

const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
公共,私有与受保护的修饰符

public:默认值,可以自由的访问类中定义的成员
private:不能在声明它的类的外部访问;只用两个类中都有相同private成员,且来自同一处声明时,这两个类型才是兼容的,protected成员也一样
protected:protected成员在派生类中仍然可以访问

其他知识点

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

2、存取器
TypeScript支持通过getters/setters来截取对对象成员的访问

3、静态属性
创建类的静态成员,这些属性存在于类本身上面而不是类的实例上

4、抽象类
abstract,抽象类做为其它派生类的基类使用

函数

普通函数和匿名函数

// Named function
function add(x, y) {
    return x + y;
}

// Anonymous function
let myAdd = function(x, y) { return x + y; };
this和箭头函数

箭头函数能保存函数创建时的 this值,而不是调用时的值

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        // 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};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);
泛型

使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据

Hello World

不使用any类型是因为any会导致这个函数可以接收任何类型的arg参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的

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

类型推论 :编译器会根据传入的参数自动地帮助我们确定T的类型

let output = identity("myString"); 
泛型约束

<T extends Lengthwise>:这是泛型约束,表示类型 T 必须满足 Lengthwise 接口的要求

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  
    return arg;
}
0

评论 (0)

取消