TypeScript学习
基础类型
- 任意值(Any): 在编程阶段还不清楚类型的变量指定一个类型,这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。
let notSure: any = 4;
notSure = "maybe a string instead";
- 数字(Number): TypeScript 里的所有数字都是浮点数。这些浮点数的类型是 number
let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744; // 八进制
let decLiteral: number = 6; // 十进制
let hexLiteral: number = 0xf00d; // 十六进制
- 布尔类型(boolean):表示逻辑值:true 和 false。
let flag: boolean = true;
- 字符串(string):一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式
let name: string = "Runoob";
let years: number = 5;
let words: string = `您好,今年是 ${ name } 发布 ${ years + 1} 周年`;
- 数组(Array): TypeScript 有两种方式可以定义数组。第一种是可以在元素类型后面接上 [],表示由此类型元素组成的一个数组
let list: number[] = [1, 2, 3];
第二种方式是使用数组泛型,Array<元素类型>:
let list: Array<number> = [1, 2, 3];
let arr: Array<any>;
arr = [1, 'a', true, { d: 4 }, [5, 6]];
// 或者
let arr: any[];
arr = [1, 'a', true, { d: 4 }, [5, 6]];
let arr: Array<object>;
// 正确,数组包含对象字面量
arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
- 元组(Tuple): 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
let x: [string, number];
x = ['hello', 10];
- 枚举(Enum): TypeScript 提供了 enum 类型,用于取值被限定在一定范围内的场景
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
- 空值(Void): 某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型
function warnUser(): void {
console.log("This is my warning message");
}
- Null 和 Undefined: TypeScript 里,undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null。
let u: undefined = undefined;
let n: null = null;
- Never: never 类型表示的是那些永不存在的值的类型。例如,never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
function error(message: string): never {
throw new Error(message);
}
类型断言(Type Assertion)
类型断言可以用来手动指定一个值的类型,即允许变量从一种类型更改为另一种类型。
<类型>值 或者 值 as 类型
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
类型推断
当类型没有给出时,TypeScript 编译器利用类型推断来推断类型。
如果由于缺乏声明而不能推断出类型,那么它的类型被视作默认的动态 any 类型。
var num = 2; // 类型推断为 number
console.log("num 变量的值为 "+num);
num = "12"; // 编译错误
console.log(num);
// 变量类型推断
let x = 10; // x 被推断为 number 类型
let y = "hello"; // y 被推断为 string 类型
// 函数返回类型推断
function add(a: number, b: number) {
return a + b; // 函数返回类型被推断为 number
}
// 数组元素类型推断
let numbers = [1, 2, 3]; // numbers 被推断为 number[] 类型
// 对象类型推断
let obj = { name: "Alice", age: 25 }; // obj 被推断为 { name: string; age: number; } 类型
// 泛型类型推断
function identity<T>(arg: T): T {
return arg;
}
let output = identity("myString"); // output 被推断为 string 类型
TypeScript 联合类型
表示一个变量或属性可以是几种不同类型的值之一。使用管道符 | 来分隔不同类型,从而创建一个联合类型。
// 基本联合类型
type StringOrNumber = string | number;
// 使用联合类型
function printId(id: StringOrNumber) {
console.log("Your ID is: " + id);
}
printId(101); // 正确
printId("202"); // 正确
printId([123]); // 错误,因为 [123] 既不是 string 也不是 number
在处理联合类型时,TypeScript 会要求你确保类型安全。例如,如果你有一个联合类型 string | number,你不能在不确定类型的情况下访问仅存在于 string 类型的属性或方法:
function getLength(value: string | number): number {
return value.length; // 错误,因为 number 类型没有 length 属性
}
function getLengthSafe(value: string | number): number {
if (typeof value === "string") {
return value.length; // 正确,现在 TypeScript 知道 value 是 string 类型
} else {
return value.toString().length; // 正确,将 number 转换为 string
}
}
TypeScript 接口
TypeScript 中的接口(Interfaces)是一种定义对象类型的方式。接口可以用来描述对象的结构,包括它有哪些属性、属性的类型以及可选属性、只读属性等。接口还可以用来描述函数类型。
// 定义一个简单的接口
interface Person {
name: string;
age: number;
}
// 使用接口作为对象的类型
let person: Person = {
name: "Alice",
age: 30
};
// 错误示例,因为缺少 age 属性
let invalidPerson: Person = {
name: "Bob"
// age: 25 // 如果加上这行,就是正确的
};
接口可以包含可选属性,通过在属性名后添加 ? 来表示:
interface Person {
name: string;
age: number;
email?: string; // 可选属性
}
let person: Person = {
name: "Alice",
age: 30
// email 是可选的,所以可以不提供
};
接口也可以包含只读属性,通过在属性名前添加 readonly 来表示:
interface Person {
readonly name: string;
age: number;
}
let person: Person = {
name: "Alice",
age: 30
};
person.name = "Bob"; // 错误,因为 name 是只读属性
接口还可以用来描述函数类型,通过在接口内部定义一个调用签名
interface AddFunction {
(a: number, b: number): number;
}
let add: AddFunction = function (a: number, b: number) {
return a + b;
};
接口的一个重要特性是它们可以扩展(extends)其他接口,
这意味着你可以从一个接口中继承属性和方法,并添加新的或覆盖旧的属性和方法:
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square: Square = {
color: "blue",
sideLength: 10
};
TypeScript 类
TypeScript 是 JavaScript 的超集,它扩展了 JavaScript 的语法,包括对类(classes)的支持。TypeScript 中的类与 JavaScript ES6 中的类非常相似,但是 TypeScript 提供了一些额外的特性,如类型注解和访问修饰符。
TypeScript 类的几个关键特性如下:
构造函数(Constructor):用于在创建类的新实例时初始化对象。
属性(Properties):类实例的变量。
方法(Methods):类实例的函数。
访问修饰符(Access Modifiers):TypeScript 提供了 public、private 和 protected 来控制类成员的访问级别。
public:默认值,允许在类的内外部访问。
private:只允许在类的内部访问。
protected:允许在类内部和继承的子类中访问。
继承(Inheritance):TypeScript 类可以使用 extends 关键字继承另一个类。
class Greeter {
greeting: string;//属性
//构造函数
constructor(message: string) {
this.greeting = message;
}
//方法
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
console.log(greeter.greet());
//继承 class child_class_name extends parent_class_name
class Animal {
move() {
console.log("Moving along!");
}
}
class Dog extends Animal {
bark() {
console.log("Woof! Woof!");
}
}
const dog = new Dog();
dog.move(); // 输出 "Moving along!"
dog.bark(); // 输出 "Woof! Woof!"
//静态属性和方法(Static Properties and Methods):使用 static 关键字定义的属性和方法属于类本身,而不是类的实例。
class MyClass {
static myStaticProperty = 42;
static myStaticMethod() {
return `Static method has been called. The static property is ${MyClass.myStaticProperty}.`;
}
}
console.log(MyClass.myStaticProperty); // 输出 42
console.log(MyClass.myStaticMethod()); // 输出 "Static method has been called. The static property is 42."
//私有
class Encapsulate {
str1:string = "hello"
private str2:string = "world"
}
var obj = new Encapsulate()
console.log(obj.str1) // 可访问
console.log(obj.str2) // 编译错误, str2 是私有的
TypeScript 对象
在 TypeScript 中,对象(Objects)是键值对的集合,用于存储各种数据的复杂实体。TypeScript 提供了丰富的语法和类型系统来描述对象的形状和行为。
对象字面量
对象字面量是一种创建对象的表达式,它由大括号 {} 包围,里面包含零个或多个键值对。每个键是一个字符串或符号(在 TypeScript 中通常不使用符号作为键),每个值可以是任何类型的数据。
interface Person {
firstName: string;
lastName: string;
age: number;
}
let person: Person = {
firstName: "张",
lastName: "三",
age: 30
};
//属性的读取和修改 使用点语法(.)或中括号语法([])来读取或修改对象的属性
console.log(person.firstName) 或者 console.log(person['age]);
展开运算符
展开运算符(…)可以将一个对象展开到另一个对象中。
let person = {
firstName: "张",
lastName: "三",
age: 30
};
let updatedPerson = {
...person,
age: 31 // 覆盖原有的 age 属性
};
console.log(updatedPerson); // { firstName: "张", lastName: "三", age: 31 }
解构赋值
解构赋值允许你从对象中提取属性,并赋值给变量。
let { firstName, lastName } = person;
console.log(firstName); // "张"
console.log(lastName); // "三"
TypeScript 泛型
泛型(Generics)是一种编程语言特性,允许在定义函数、类、接口等时使用占位符来表示类型,而不是具体的类型。
泛型是一种在编写可重用、灵活且类型安全的代码时非常有用的功能。
使用泛型的主要目的是为了处理不特定类型的数据,使得代码可以适用于多种数据类型而不失去类型检查。
泛型的优势包括:
- 代码重用: 可以编写与特定类型无关的通用代码,提高代码的复用性。
- 类型安全: 在编译时进行类型检查,避免在运行时出现类型错误。
- 抽象性: 允许编写更抽象和通用的代码,适应不同的数据类型和数据结构。
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString"); // 明确指定泛型类型为 string
let output2 = identity("myString"); // 利用类型推论,自动确定泛型类型为 string
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
let swapped = swap<string, number>(["hello", 42]); // 类型为 [number, string]
//泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
//泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
constructor(zeroValue: T, add: (x: T, y: T) => T) {
this.zeroValue = zeroValue;
this.add = add;
}
}
let myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y);
TypeScript 命名空间
在 TypeScript 中,命名空间(Namespaces)是一种组织代码的方式,它可以避免全局命名空间的污染,并且提供了一种将代码分割成可重用的组件的方法。命名空间在 TypeScript 中是通过 namespace 关键字来声明的。
namespace MyNamespace {
export interface Person {
name: string;
age: number;
}
export class Greeter {
constructor(public person: Person) {}
greet() {
return `Hello, ${this.person.name}!`;
}
}
}
let person: MyNamespace.Person = {
name: "Alice",
age: 30
};
let greeter = new MyNamespace.Greeter(person);
console.log(greeter.greet()); // 输出 "Hello, Alice!"