惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
V
V2EX
T
The Blog of Author Tim Ferriss
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Hugging Face - Blog
Hugging Face - Blog
Stack Overflow Blog
Stack Overflow Blog
I
InfoQ
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Last Week in AI
Last Week in AI
Recent Announcements
Recent Announcements
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tailwind CSS Blog
美团技术团队
Martin Fowler
Martin Fowler
宝玉的分享
宝玉的分享
Blog — PlanetScale
Blog — PlanetScale
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
J
Java Code Geeks

博客园 - 小y

HP-Socket压力测试例子,用恐怖如斯来形容。 经典分类算法KNN的研究 用python自己回测股票策略 怎么让大模型查询本地数据库输出结果(Dify+豆包) OpenClaw工作原理解析【建议背诵】 AiAgent工具之网页搜索 怎么在百度搜索中屏蔽csdn 使用C#调用Yolo26模型的ONNX ESP32 使用MicroPython编写代码实现远程控制LED灯 使用Razor模板引擎实现自动生成代码 ESP32开发环境下载、USB驱动安装 2026年最新年会抽奖小程序 grafana+prometheus快速实现可视化大屏 PyAutoGUI库自动化测试脚本工具模拟键盘鼠标操作 性能卓越的开源时序数据库——QuestDB GitHub超 30000+ star , 超强大的开源项目Supervision DevExpress系列:dxValidationProvider和dxErrorProvider两种控件的用法和区别 实测:MySQL跑在Docker里会损失多少性能?寻找Docker优化点 用C# GDI编写粒子效果 动画图解嵌入式常见的通讯协议:SPI、I²C、UART、红外 C#中的MVVM框架 .net 9 中的WinUI3和MAUI有什么区别 Netty的高性能之道 一文搞定理解RPC CSS flex布局(弹性布局/弹性盒子)
为什么选择使用TypeScript?这篇文章讲透了
小y · 2022-12-29 · via 博客园 - 小y

类型系统

众所周知 JS 是一门 弱类型语言,不到执行时都不能确定变量的类型。编码时可以随心所欲反正不报错,一不小心就写出八哥( undefined 警告!)。

1. 静态类型检查

静态类型检查让 TS 在编辑器中披上 强类型语言 的“马甲”,使得开发者在 编码时 就可以 避免大多数类型错误的情况发生,而开发者要做的就 只是声明变量时多写一个符号和一个单词

当然你也可以在声明变量时 不指定类型 或者使用 any 类型 来达到 JS 的动态类型效果,让 TypeScript 变成 AnyScript ,任性~

let name: string = '陈皮皮';
name = 9527; // 报错

let age: any = 18;
age = 'eighteen'; // 不报错

2. 原始类型

TS 在支持 与 JS 基本相同的原始类型 之外,还额外提供了 枚举(Enum)和元组(Tuple) 的支持。

// 枚举
enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}
let direction: Direction = Direction.Up;

// 元组
let x: [string, number];
x = ['hello', 10]; // 不报错
x = [10, 'hello']; // 报错

3. 智能提示

类型系统 配合 声明文件(关于声明文件我们后面再聊)给我们带来了编辑器中 完善的自动补全智能提示,大大增加了开发效率,也再不会因为拼错变量名或函数名而导致运行时的错误。

修饰符和静态关键字

泪目,是从 C# 那里几乎原汁原味搬过来的一套修饰符和关键字,主要如以下几个:

1. 访问修饰符(public、private 和 protected)

用来 限定类成员的可访问范围

没有 internal 和 protect internal

没有访问修饰符的封装莫得灵魂!

class Me {
    public name = '陈皮皮'; // 大家都知道我叫陈皮皮
    private secret = '*******'; // 我的秘密只有我知道
    protected password = '********'; // 我的支付宝密码会告诉我的后人的
}

let me = new Me();
let a = me.name; // 拿到了我的名字
let b = me.secret; // 报错,私有的属性
let c = me.password; // 报错,受保护的属性

class Child extends Me {
    constructor() {
        super();
        this.name = '陈XX';
        this.secret // 报错,无法访问
        this.password = '888888'; // 可以访问
    }
}

2. 静态关键字(static)

用于 定义全局唯一的静态变量和静态函数

class Whatever {
    public static origin: string = 'Whatever';
    public static printOrigin() {
        console.log(this.origin);
        console.log(Whatever.origin);
    };
}

console.log(Whatever.origin); // Whatever
Whatever.printOrigin(); // Whatever

3. 抽象关键字(abstract)

用来定义 抽象类或抽象函数,面向对象编程很重要的一环。

abstract class Animal {
    abstract eat(): void; // 不同动物进食的方式不一样
}

let animal = new Animal(); // 报错,法实例化抽象类无

class Dog implements Animal {
    eat() {
        console.log('我吃,汪!');
    }
}

let dog = new Dog();
dog.eat(); // 我吃,汪!

class Cat implements Animal {
    // 报错了,没有实现进食的功能
}

4. 只读关键字(readonly)

用来定义只读的字段,使得字段 只能在创建的时候赋值一次

class Human {
    name: string;
    readonly id: number;
    constructor(name: string, id: number) {
        this.name = name;
        this.id = id;
    }
}

let human = new Human('陈皮皮', 666666);
human.name = '陈不皮'; // 名字可以改
human.id = 999999; // 报错,身份证号码一旦确定不能更改

接口(Interface)

C# 和 Java 的朋友们让我看到你们的双手好吗

接口用于一系列成员的声明,但不包含实现,接口支持合并(重复声明),也可以继承于另一接口。

下面展示几个常见的用法:

1. 扩展原始类型

// 扩展 String 类型
interface String {

    /**
     * 翻译
     */
    translate(): string;

}

// 实现翻译函数
String.prototype.translate = function () {
    return this; // 不具体写了,直接返回原字符串吧
};

// 使用
let nickname = '陈皮皮'.translate();

2. 定义类型

interface Human {
    name: string; // 普通属性,必须有但是可以改
    readonly id: number; // 只读属性,一旦确定就不能更改
    hair?: number; // 可选属性,挺秃然的
}

let ChenPiPi: Human = {
    name: '陈皮皮',
    id: 123456789,
    hair: 9999999999999
}

3. 类实现接口

interface Vehicle {
    wheel: number;
    engine?: string;
    run(): void;
}

class Car implements Vehicle {
    wheel: 4;
    engine: '帝皇引擎';
    run() {
        console.log('小汽车跑得快!')
    }
}

class Bike implements Vehicle {
    wheel: 2;
    run() {
        console.log('小黄车冲冲冲!')
    }
}

类型别名(Type)

这是一个比较常用的特性,作用如其名。

类型别名 用来 给类型起一个新的名字

类型别名和接口很相似,类型别名可以作用于原始类型,联合类型,元组以及其它任何你需要手写的类型,接口支持合并而类型别名不可以。

类型别名同样也 支持扩展,并且可以和接口互相扩展。

// 给原始类型起个小名
type UserName = string;
let userName: UserName = '陈皮';

// 还可以是函数
type GetString = () => string;
let getString: GetString = () => {
    return 'i am string';
}
let result = getString();

// 创建一个新的类型
type Name = {
    realname: string;
    nickname: string;
}
let name: Name = {
    realname: '吴彦祖',
    nickname: '陈皮皮'
}
// 再来一个新的类型
type Age = {
    age: number;
}
// 用上面两个类型扩展出新的类型
type User = Name & Age;
let user: User = {
    realname: '吴彦祖',
    nickname: '陈皮皮',
    age: 18,
}

联合类型(Union Types)

使用 联合类型 允许你在 声明变量或接收参数时兼容多种类型

1. 表示一个值可以是几种类型之一

let bye: string | number;
bye = 886; // 不报错
bye = 'bye'; // 不报错
bye = false; // 报错

2. 让函数接受不同类型的参数,并在函数内部做不同处理

function padLeft(value: string, padding: string | number) {
    if (typeof padding === 'string') {
        return padding + value;
    } else {
        return Array(padding + 1).join('') + value;
    }
}

padLeft('Hello world', 4); // 返回 '    Hello world'
padLeft('Hello', 'I said: '); // 返回 'I said: Hello'

泛型(Generics)

C# 和 Java 的朋友们再次让我看到你们的双手好吗

使用 泛型 可以让一个 类/函数支持多种类型的数据,使用时可以传入需要的类型

又是一个非常实用的特性,利用泛型可以 大大增加代码的可重用性,减少重复的工作,点赞!

以下是两个常用的用法:

1. 泛型函数

// 这是一个清洗物品的函数
function wash<T>(item: T): T {
    // 假装有清洗的逻辑...
    return item;
}

class Dish { } // 这是盘子
let dish = new Dish(); // 来个盘子
// 盘子洗完还是盘子
// 用尖括号提前告诉它这是盘子
dish = wash<Dish>(dish);

class Car { } // 这是汽车
let car = new Car(); // 买辆汽车
// 汽车洗完还是汽车
// 没告诉它这是汽车但是它认出来了
car = wash(car);

2. 泛型类

// 盒子
class Box<T>{
    item: T = null;
    put(value: T) {
        this.item = value;
    }
    get() {
        return this.item;
    }
}

let stringBox = new Box<String>(); // 买一个用来装 String 的盒子
stringBox.put('你好!'); // 存一个 '你好!'
// stringBox.put(666); // 报错,只能存 String 类型的东西
let string = stringBox.get(); // 拿出来的是 String 类型

装饰器(Decorator)

这是一个相对比较高级的特性,以 @expression 的形式对类、函数、访问符、属性或参数进行额外的声明

对类做预处理

export function color(color: string) {
    return function (target: Function) {
        target.prototype.color = color;
    }
}

@color('white')
class Cloth {
    color: string;
}
let cloth = new Cloth();
console.log(cloth.color); // white

@color('red')
class Car {
    color: string;
}
let car = new Car();
console.log(car.color); // red

命名空间(namespace)

命名空间用来定义标识符的可用范围,主要用于解决重名的问题,对于项目模块化有很大的帮助。

1. 对相同名字的类和函数进行区分

// pp 命名空间
namespace pp {
    export class Action {
        public static speak() {
            cc.log('我是皮皮!');
        }
    }
}

// dd 命名空间
namespace dd {
    export class Action {
        public static speak() {
            cc.log('我是弟弟!');
        }
    }
}

// 使用
pp.Action.speak(); // 我是皮皮!
dd.Action.speak(); // 我是弟弟!

2. 对接口进行分类

namespace Lobby {
    export interface Request {
        event: string,
        other: object
        // ...
    }
}

namespace Game {
    export interface Request {
        event: string,
        status: string
        // ...
    }
}

// 用于 Lobby 的请求函数
function requestLobby(request: Lobby.Request) {
    // ...
}

// 用于 Game 的请求函数
function requestGame(request: Game.Request) {
    // ...
}

声明文件(Declaration Files)

声明文件,即以 d.ts 作为后缀的代码文件,用来声明当前环境中可用的类型。

声明文件这一特性对于 TypeScript 来说是 极其重要 的,代码编辑器中的智能提示等特性都依赖于声明文件。

可以发现目前大多数 第三方 JavaScript 库 都有声明文件,声明文件让这些库在代码编辑器中也可以 拥有类型检查智能提示等特性,使用体验 Max 。