按照TS官网的介绍,ts是一门javaScript的超集。所谓超集意思就是,js有的东西,我ts有,js没有的,我ts还有,那到底ts提供给了我们哪些额外的东西了呢,先让我们看下js这门语言的简单描述,然后一切就明白了。
js的语言特点
JavaScript是一种解释性、动态、弱类型的事件驱动语言,支持原型继承和函数式编程,广泛应用于异步编程,并且跨平台性能强
var x = 2;
x = "sdsdsd";
这样声明的变量可以修改类型一开始是number类型,后面被修改成了string类型
TS提供的功能
TypeScript提供了静态类型、面向对象编程和现代化 JavaScript 特性支持,同时具备装饰器、声明文件等功能,使得开发者能够编写可靠且高效的大型应用程序。其强大的工具支持和类型推断功能有助于提高代码的可维护性和可读性,同时加快开发速度
TS的使用
基础类型
对于js原有的那些数据类型,在用ts声明时,只需在变量后加一个冒号以及类型名即可。
// 字符串类型
let name:string = 'typescript';
// 数字类型
let age: number = 18;
// 此时如果给age赋值其它类型,就会报错
age = '18' // Type 'string' is not assignable to type 'number'.
// 布尔类型
let isFlag:boolean = false
// Null类型
let isNull:null = null
// undefiend类型
let isUnde:undefined = undefined
// symbol类型
let id:symbol = Symbol(1)
// 对象类型
let peolpe:object = {
id
name,
age
}
// 数组类型,定义一个数组,且组内成员的类型为数字
let list:number[] = [1,2,3]
ts中还新增了枚举和元祖
其中枚举类型确实是个非常有用的类型,它可以为一个值赋予一个有意义的描述
在js中是这样的用法
let userType
switch(userType){
case 1:
// 学生用户的代码逻辑
case 2:
// 家长用户的代码逻辑
case 3:
// 老师用户的代码逻辑
}
上面这段代码可能过段时间就不知道这段代码是什么意思了,而ts中的枚举就可以很方便的看懂了
那么在ts中怎么用呢,下面的用法
enum UserType {
student = 1,
parents = 2,
teacher = 3
}
let userType:UserType
switch(userType){
case UserType.student:
// 学生用户的代码逻辑
case UserType.parents:
// 家长用户的代码逻辑
case UserType.teacher:
// 老师用户的代码逻辑
}
上面的代码是不是一目了然呢,这样编写代码是不是很舒服呢
接口
//对象接口
interface Point {
name: string;
age: number;
sex: string;
hobby?: string[];//这个问号代表着这个参数可选
}
//interface这个是接口声明的关键字 后面的跟的是接口名
//其中内容是前面的就是在这接口中name的类型就是string,下面同理
var use: Point = {//其中use的中
name: "李四",
age: 68,
sex: "男",
hobby: ["html", "css", "javascript"],//这个参数可写可不写
};
类型推断&类型断言&非空断言
在 TypeScript 的世界,类型推断、类型断言和非空断言是我们日常开发中经常遇到的概念。它们为我们提供了处理类型不确定性的手段,在某些情况下,它们可以极大地提高代码的可读性和可维护性。
类型推断
类型推断的含义是不需要指定变量类型或函数的返回值类型,TypeScript 可以根据一些简单的规则推断其类型。这种特性使得我们在编写代码时可以更加灵活,同时也减少了代码的冗余性。例如:
let x = 3; // TypeScript 推断 x 是 number 类型
在这个例子中,我们没有显式地声明 x 的类型,但 TypeScript 会根据赋值的情况自动推断出 x 的类型为 number。
类型断言
类型断言主要用于当 TypeScript 推断出来类型并不满足你的需求,你需要手动指定一个类型。TypeScript 允许你覆盖它的推断,毕竟作为开发者你比编译器更了解你写的代码。使用关键字 as 进行类型断言是最常见的做法,而标签 <> 的方式容易与 JSX 语法冲突,因此建议统一使用 as 进行类型断言。示例如下:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
在这里,我们将 someValue 断言为 string 类型,以便能够调用 length 属性。
非空断言
如果编译器不能够去除 null 或 undefined,可以使用非空断言 ! 手动去除。这在处理 DOM 元素等可能为 null 或 undefined 的情况时非常有用。
const element = document.getElementById("myInput")!;
element.focus();
在这个例子中,我们使用了非空断言符号 ! 来告诉 TypeScript,getElementById 肯定会返回一个非空的元素,这样我们可以直接调用 focus 方法而无需担心空值异常。
特别注意: TypeScript 无法像 JavaScript 那样访问 DOM,因此每当我们尝试访问 DOM 元素时,TypeScript 都无法确定它们是否真的存在。使用非空断言运算符 (!) 或类型断言 (as),我们可以明确地告诉编译器一个表达式的值不是 null 或 undefined,或者转换类型。
总之,类型推断、类型断言和非空断言为我们在使用 TypeScript 时提供了更多的灵活性和安全性。合理地运用它们可以帮助我们更好地应对类型不确定性的情况,使得代码更加可靠和清晰。
接口(Interfaces)
接口是 TypeScript 中非常重要的概念,它类似于一种规范或契约,用于约定对象的结构。通过接口,我们可以规定对象应包含哪些成员以及这些成员的类型。
基本用法
interface Post {
title: string;
content: string;
}
function printPost(post: Post) {
console.log(post.title);
console.log(post.content);
}
printPost({
title: 'Hello TypeScript',
content: 'A JavaScript superset'
});
在上述示例中,Post
接口定义了一个对象应当具有 title
和 content
两个属性。函数 printPost
的参数 post
必须符合 Post
接口的约定。
可选成员
如果一个对象的某些成员是可有可无的,我们可以使用可选成员的特性:
interface Post {
title: string;
content: string;
subtitle?: string; // 可选成员
}
只读成员
接口可以包含只读成员,一旦初始化后就不能再修改:
interface Post {
title: string;
content: string;
subtitle?: string; // 可选成员
readonly summary: string; // 只读成员
}
动态成员
动态成员通常用于一些带有动态键值的对象,例如缓存对象等。
interface Cache {
[prop: string]: string; // 动态成员
}
const cache: Cache = {};
cache.foo = 'value1';
cache.bar = 'value2';
在上述例子中,我们创建了一个 Cache 接口,并实现了一个带有动态成员的 cache 对象,这些动态成员都必须符合 Cache 接口的类型约束。
总之,通过接口,我们可以明确地约定对象的结构和约束其类型。虽然在运行阶段接口本身没有意义,但它在开发阶段能够帮助我们更好地进行类型检查和约束,提高代码的可维护性和可读性。
class类
类描述了所创建的对象共同的属性和方法。通过 class 关键字声明一个类,主要包含以下模块:属性、构造函数和方法。
类的本质
在 TypeScript 中,类是一种用来创建对象的蓝图。它定义了对象共同的属性和方法。通过 class 关键字声明一个类,它包含属性、构造函数和方法。类可以被实例化为对象,然后可以访问对象的属性和方法。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(): string {
return '我会动';
}
}
const p1 = new Animal('李焕英');
console.log(p1.move()); // 输出:"我会动"
constructor()
方法是类的默认方法,通过 new
来生成对象实例时,自动调用该方法。换句话说,constructor()
方法默认返回实例对象 this
。
类的继承
类继承是一种基于类的程序设计模式,允许使用继承来扩展现有的类,以便子类可以复用父类的公共部分。在 TypeScript 中,通过 extends 关键字实现类的继承。子类会继承父类的属性和方法,同时可以添加自己的属性和方法。
使用 extends
关键字来实现继承:
// 继承 Animal 类
class People extends Animal {
color: string;
constructor(name: string, color: string) {
super(name); // 调用父类的构造函数
this.color = color;
}
coding(): string {
return '我会写代码';
}
}
const p2 = new People('张三峰', '黄皮肤');
console.log(p2.move()); // 输出:"我会动"
console.log(p2.coding()); // 输出:"我会写代码"
类方法重载
类方法重载是指在类中对方法进行多态的一种表现形式。在 TypeScript 中,方法重载可以根据不同的参数类型执行不同的操作。这使得方法可以根据传入的参数类型进行不同的逻辑处理。比如,在以下示例中我们重载了 Animal1
类的 move
成员方法:
class Coder {
coding(): void;
coding(type: string): void;
coding(type: string[]): void;
coding(type?: string | string[]) {
if (typeof type === 'string') {
console.log('我会写' + type);
} else if (!type) {
console.log('我会敲代码');
} else {
console.log('我会写' + type.join(','));
}
}
}
const c = new Coder();
c.coding();
c.coding('javascript');
c.coding(['html', 'css', 'javascript']);
类的修饰符
作用: 对类的属性和方法提供权限控制
public: 公有的可以在任何地方可以被访问和修改(类中属性和方法默认为 public)
private: 私有的仅在当前类可访问和修改
protected : 仅当前类与子类(继承类)可以访问和修改
readonly: 只读修饰符, 仅可访问不可修改
class Animal2 {
public name: string;
readonly body: boolean;
protected color: string;
private height: number;
constructor(name: string, body: boolean, color: string, height: number) {
this.name = name;
this.body = body;
this.color = color;
this.height = height;
}
say(): void {
console.log(`我叫${this.name},我有${this.body},我皮肤是${this.color},我身高${this.height}`);
}
}
const p4 = new Animal2('李焕英', true, '黄皮肤', 180);
静态属性和静态方法
静态属性和静态方法不需要实例化类即可访问,它们属于类本身而不是类的实例。这意味着静态属性和方法不依赖于类的实例,可以直接通过类名进行访问和调用。静态属性和方法通常用于表示与类本身相关的行为或特征。
class Animal3 {
// 定义静态属性
static categoies: string[] = ['人类','中国人']
// 定义静态方法
static isChinese(a: any) {
return a instanceof Animal3 // 判断 a 是否为 Animal 的实例
}
}
// 注意! 访问 静态属性和方法不需要实例化
// 访问静态属性 categoies
console.log(Animal3.categoies)
以上都是 TypeScript 中类的重要概念,在实际开发中,它们可以帮助我们更好地组织代码并实现面向对象编程中的各种特性。
泛型约束
泛型在 TypeScript 中提供了一种定义函数、接口和类的方式,而不需要指定具体类型。相反,在使用函数、接口或类时才会定义类型。这个特性允许高度的代码重用性。
不使用泛型:
让我们以创建特定类型的数组为例
// 定义一个创建 number 类型的方法
function createNumberArray(length: number, value: number): number[] {
const arr = Array<number>(length).fill(value);
return arr;
}
// 定义一个创建 string 类型的方法
function createStringArray(length: number, value: string): string[] {
const arr = Array<string>(length).fill(value);
return arr;
}
const numArr = createNumberArray(3, 100);
const strArr = createStringArray(3, 'foo');
使用泛型:
通过引入泛型参数 T
,我们可以在函数内部代表未指定的类型,并在调用函数时指定类型
function createArray<T>(length: number, value: T): T[] {
const arr = Array<T>(length).fill(value);
return arr;
}
const numArr = createArray<number>(3, 100);
const strArr = createArray<string>(3, 'foo');
在这种情况下,T 表示在调用函数时将确定的类型。这种方法利用泛型的能力创建灵活且可重用的函数,可以处理各种数据类型。
在 TypeScript 中,Array 类本身就是一个泛型类型。当定义 Array 类型时,它不知道将存储什么类型的数据,因此使用了泛型参数。在使用 Array 类型时指定了该参数,展现了泛型的本质。总之,泛型使我们的代码更加抽象和可重用,可以推迟类型的具体指定直到使用时。通过使用泛型,我们可以编写更加灵活和通用的代码,适用于不同的数据类型。
工具类型
在 TypeScript 中,工具类型提供了一系列强大的功能,可以帮助我们操作和转换现有的类型。这些工具类型允许我们以更高级的方式操作类型,使得代码更加灵活、可维护和安全。
Partial< T >
Partial 是一个预定义的 TypeScript 工具类型,它接受一个类型 T 并返回一个新类型,其中 T 的所有属性变为可选。这样的特性在创建包含可选属性的对象时非常有用。
interface User {
name: string;
age: number;
}
function updateUser(user: User, updates: Partial<User>): User {
return { ...user, ...updates };
}
const initialUser: User = { name: "Alice", age: 30 };
const updatedUser = updateUser(initialUser, { age: 31 });
Required< T >
与 Partial
相反的是 Required
工具类型,它接受类型 T
并返回一个新类型,该类型中 T
的所有属性都变为必填项。
interface Props {
name?: string;
age?: number;
}
function validateProps(props: Required<Props>) {
// ...
}
Pick<T, K>
Pick
工具类型接受两个参数:类型 T
和 K
,它从类型 T
中挑选出指定的属性集合 K
构造出一个新类型。
interface Car {
make: string;
model: string;
year: number;
}
type CarSummary = Pick<Car, "make" | "model">;
Omit<T, K>
相对于 Pick
,Omit
接受类型 T
和 K
,但返回结果是从 T
中剔除了指定属性集合 K
后的新类型。
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Omit<Todo, "description">;
ReturnType< T >
ReturnType
工具类型接受一个函数类型,并返回其返回值的类型。
function greet(): string {
return "Hello!";
}
type GreetReturnType = ReturnType<typeof greet>; // GreetReturnType is string
通过使用 TypeScript 提供的工具类型,我们可以以一种更高级、更复杂的方式操作和利用类型系统,从而增强代码的健壮性和可读性。这些工具类型提供了便捷的方式来创建、转换和操纵类型,使得 TypeScript 在静态类型检查方面能够提供更多的支持,确保编写的代码更加可靠。