苟日新,日日新,又日新
let welcome = 'hello';
welcome();
const str = Date.now() % 2 ? '奇数' : '偶数';
if (str !== '奇数') {
alert('hello');
} else if (str === '偶数') {
alert('world');
}
const obj = { width: 10, height: 15 };
const area = obj.width * obj.heigth;
const message = 'hello,world';
message.toUperCase();
浏览器不能直接运行 TypeScript 代码,需要编译为 JavaScript 再交由浏览器解析器执行
npm i typescript -g
tsc demo.ts
创建 TypeScript 编译控制文件
tsc --init
监视目录中 .ts 文件变化
tsc --watch
# 当编译出错时不生成 .js 文件
tsc --noEmitOnError --watch
# 也可以通过 tsconfig.json 中的 noEmitOnError 配置
const/let 变量/方法: 类型let a: 'hello', a 的值就只能是字符串 ‘hello’,不过实际开发中用的不多let str = 'hello' ,str 只是字符串,但是可以访问 str.lengthlet a: any;
a = 9;
a = 'hello';
a = false;
// 隐式 any
// 声明变量但不赋值,TypeScript 推断变量类型为 any
// 注: 类型推断是声明变量并赋值,TypeScript 主动推断变量所属类型
let b;
b = 9;
b = 'hello';
b = false;
let a: any;
a = 9;
let b: string;
b = a; // 无警告
let a: unknown = 'hello';
let b: string;
// 方法一: 类型判断
if (typeof a === 'string') {
b = a;
}
// 方法二: 断言
b = a as string;
b = <string>a;
let a: unknown = 'hello';
(a as 'string').toUpperCase();
function demo(): never {
throw new Error('程序运行异常!');
}
let a: string = 'hello';
if (typeof a !== 'string') {
console.log(a); // 鼠标放在 a 上面显示: let a: never
}
虽然返回值是 undefined,但是和定义 undefined 类型方法返回值不同
function demo(): void {}
let res = demo();
// 此处使用 res 会飘红: 无法测试 "void" 类型的表达式的真实性
// ps: 只有条件改为 res === undefined 不会飘红
// 但是没有任何意义,看到 void 的方法,就不要关注和使用它的返回值
if (res) {
}
function demo(): undefined {}
let res = demo();
// 此处正常
if (res) {
}
void 的空是概念上的空,undefined 是空的具体实现之一,可以说 undefined 是 void 能接受的空的一种形式,void 表达的语义超过单纯的 undefined,它是一种意图上的约定(不要使用方法返回值),而不仅仅是特定值的限制
let arr1: string[]
let arr2: Array<number>
arr1 = ['a', 'b', 'c']
arr2 = [1, 2, 3]
Array<number> 属于泛型let person1: { name: string, age?: number };
// 含义同上,也能用分号做分隔
let person2: { name: string; age?: number };
// 含义同上,也能用换行做分隔
let person3: {
name: string
age?: number
};
person1 = { name: '李四', age: 18 };
let person: {
name: string;
age?: number;
[key: string]: any;
};
person = {
name: 'John',
age: 30,
gender: '男'
};
// 这个声明函数名为 count 的函数
// 只接受两个 number 的参数,返回一个 number 的结果
let count: (a: number, b: number) => number;
count = function (a, b) {
return a + b;
};
? 表示可选元素,...类型[] 表示任意多个该类型
let arr1: [string, number];
let arr2: [string, number?];
let arr3: [string, ...number[]];
arr1 = ['hello', 42];
arr2 = ['world'];
arr3 = ['foo', 1, 2, 3];
Up = 6
enum Direction {
Up = 6,
Down,
Left,
Right
}
console.log(Direction.Up);
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
console.log(Direction.Up)
const enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
console.log(Direction.Up)
// 编译结果
console.log("UP" /* Direction.Up */);
type num = number;
let a: num = 5;
type Status = number | string
let sts: Status = 200
type Gender = 'male' | 'female'
let gender: Gender = 'male'
type Area = {
width: number;
height: number;
};
type Address = {
country: string;
city: string;
street: string;
};
type House = Area & Address;
const myHouse: House = {
width: 10,
height: 20,
country: 'USA',
city: 'New York',
street: '123 Main St'
};
// 显式返回类型注解
// 严格模式,严格要求返回值为空
function demo(): void {
// return;
return undefined;
}
// 上下文类型推断
// 宽松模式,不会严格要求返回值为空,但是返回值无法使用
type demo = () => void
const demo1: demo = () => {
return 'demo1'
}
class Person {
// 属性声明
name: string
age: number
// 构造器
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
speak() {
console.log(`我的名字是 ${this.name},我今年 ${this.age} 岁。`);
}
}
class Student extends Person {
grade: string
// 构造器
// 如果没有新增属性,则可以省略构造器,直接使用父类的构造器
// 如果子类中有构造器,则必须调用 super() 来调用父类的构造器
constructor(name: string, age: number, grade: string){
super(name, age)
this.grade = grade
}
// 重写父类的方法
override speak() {
console.log(`我是学生,我的名字是 ${this.name},我今年 ${this.age} 岁,我在读 ${this.grade} 年级。`)
}
// 子类新增的方法
study() {
console.log(`${ this.name }正在努力学习中...`)
}
}
const student = new Student('小明', 20, '大三')
student.speak()
student.study()
readonly: 只读的,无法修改
// 简写前
class Person1 {
name: string
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// 简写后
class Person2 {
constructor(public name: string, public age: number) {}
}
包裹的重量是${this.weight}kg,运费是${this.calculate()}元)
}
}class StandardPackage extends Package { constructor(weight: number, public unitPrice: number) { super(weight) } calculate(): number { return this.weight * this.unitPrice } } const standardPackage = new StandardPackage(10, 5) standardPackage.print()
- 使用抽象类的场景
- 定义通用接口
- 提供基础实现
- 确保关键实现
- 共享代码和逻辑
### 13. interface
- interface 是一种定义结构的方式,主要作用是为:类、对象、函数等规定一种契约,这样可以确保代码的一致性和类型安全,但是 interface 只能定义格式,不能包含任何实现
- 定义类结构
```typescript
interface PersonInterface {
name: string;
age: number;
speak(n: number): void;
}
class Person implements PersonInterface {
constructor(public name: string, public age: number) {}
speak(n: number): void {
for (let i = 0; i < n; i++) {
console.log(`你好,我是${this.name},今年${this.age}岁。`);
}
}
}
const person = new Person("张三", 30);
person.speak(3);
interface PersonInterface {
name: string;
age?: number;
speak(n: number): void;
}
const person: PersonInterface = {
name: 'John',
age: 30,
speak(n: number): void {
for (let i = 0; i < n; i++) {
console.log(`我叫${this.name},我今年${this.age}岁了!`);
}
}
};
person.speak(3);
interface CountInterface {
(a: number, b: number): number;
}
const count: CountInterface = (a, b) => {
return a + b;
}
interface PersonInterface {
name: string;
age: number;
}
interface StudentInterface extends PersonInterface {
grade: number;
}
const student: StudentInterface = {
name: "张三",
age: 20,
grade: 90,
};
console.log(student);
interface PersonInterface {
name: string;
age: number;
}
interface PersonInterface {
gender: string;
}
const person: PersonInterface = {
name: "张三",
age: 18,
gender: "男",
};
console.log(person);
interface PersonInterface {
name: string;
age: number;
speak(): void;
}
type PersonType = {
name: string;
age: number;
speak(): void;
}
interface FlyInterface {
fly(): void;
}
interface SwimInterface {
swim(): void;
}
// 一个类可以实现多个接口
class Duck implements FlyInterface, SwimInterface {
fly(): void {
console.log("Duck is flying");
}
swim(): void {
console.log("Duck is swimming");
}
}
// 泛型可以用 T 表示,也可以是其他任何字符串
function logData1<T>(data: T) {
console.log(data);
}
logData1<string>("Hello, World!");
logData1<number>(42);
logData1<boolean>(true);
// 可以同时使用多个泛型参数
function logData2<T, U>(data1: T, data2: U) {
console.log(data1, data2);
}
logData2<string, number>("Age:", 30);
logData2<boolean, string>(true, "Is it true?");
function logData3<T, U>(data1: T, data2: U): T | U {
console.log(data1, data2);
return data1; // 或者 return data2;
}
const result1 = logData3<string, number>("Hello", 123);
interface PersonInterface<T> {
name: string;
age: number;
extraInfo: T
}
let person1: PersonInterface<string> = {
name: "John",
age: 30,
extraInfo: "Likes to play football"
}
// 定义一个接口,使用泛型来表示额外的信息类型,并且使用一个类型别名来定义额外信息的结构
interface PersonInterface<T> {
name: string;
age: number;
extraInfo: T;
}
type JobInfo = {
company: string;
position: string;
};
let person: PersonInterface<JobInfo> = {
name: 'John',
age: 30,
extraInfo: {
company: 'ABC Corp',
position: 'Software Engineer'
}
};
class Person<T> {
constructor(
public name: string,
public age: number,
public extraInfo: T
) {}
speak() {
console.log(`我是${this.name},今年${this.age}岁了。`);
console.log(`我的额外信息是:${this.extraInfo}`);
}
}
const person1 = new Person<string>('John', 30, 'Likes to play football');
person1.speak();
.d.ts,作用是为现有的 JavaScript 代码提供类型信息,使 TypeScript 在使用这些 JavaScript 库或模块的时候能够进行类型检查和提示
// demo.js
export function add(a, b) {
return a + b;
}
// demo.d.ts
declare function add(a: number, b: number): number;
export { add };
// index.ts
import { add } from './demo.js'
console.log(add(1, 2))
/**
* Function 类型的范围非常广泛,包括普通函数、箭头函数、构造函数等。为了更准确地描述构造函数类型,我们可以使用一个特殊的类型别名来表示它。
* new (...args: any[]) => {}
* new 表示该类型是一个构造函数类型,可以使用 new 操作符调用
* ...args 表示构造器可以接受任意数量的参数
* any[] 表示构造器可以接受任意类型的参数
* {} 表示返回类型是对象,非 null、非 undefined 的值
*/
type Constructor = new (...args: any[]) => {};
interface Person {
getTime(): string;
}
function LogTime<T extends Constructor>(target: T) {
return class extends target {
createTime: Date;
constructor(...args: any[]) {
super(...args);
this.createTime = new Date();
}
getTime() {
return `该对象的创建时间是: ${this.createTime}`;
}
};
}
// @LogTime 等价于 LogTime(Person)
@LogTime
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
speak() {
console.log(`我的名字是${this.name},今年${this.age}岁了`);
}
}
const p1 = new Person('张三', 20);
console.log(p1);
console.log(p1.getTime());
@LogInfo(3) class Person { constructor( public name: string, public age: number ) {} } const p1 = new Person(‘张三’, 20); p1.introduce();
### 装饰器组合
- 装饰器可以组合使用,顺序是,先从上到下执行所有的装饰器工厂,依次获取到装饰器,再从下到上执行所有的装饰器
```typescript
function test1(target: Function) {
console.log('test1')
}
function test2() {
console.log('test2工厂')
return function (target: Function) {
console.log('test2')
}
}
function test3() {
console.log('test3工厂')
return function (target: Function) {
console.log('test3')
}
}
function test4(target: Function) {
console.log('test4')
}
@test1
@test2()
@test3()
@test4
class Person{}
/**
* 输出结果:
* test2工厂
* test3工厂
* test4
* test3
* test2
* test1
*/
__${propertyKey}
Object.defineProperty(target, propertyKey, {
get() {
return this[key]
},
set(newValue) {
console.log(${propertyKey} 的值被设置为: ${newValue})
this[key] = newValue
},
enumerable: true, // 可枚举
configurable: true // 可配置
})
}
class Person {
name: string
@State age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}const p1 = new Person(‘张三’, 18) const p2 = new Person(‘李四’, 20) p1.age = 19 p2.age = 21 console.log(p1.age) // 输出: 19 console.log(p2.age) // 输出: 21
### 方法装饰器
- 装饰器装饰方法时,会得到三个参数,target、方法名称(propertyKey)和方法的描述对象(descriptor)
- target 对于静态方法来说值是类,对于实例方法来说值是原型对象
- descriptor 方法的描述对象,其中的 value 属性是被装饰的方法
```typescript
function Logger(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`调用了${propertyKey}方法,参数是:${args.join(', ')}`);
return original.call(this, ...args);
};
}
class Person {
constructor(
public name: string,
public age: number
) {}
@Logger speak(str: string) {
console.log(`我的名字是${this.name},今年${this.age}岁了${str}`);
}
static isAdult(age: number): boolean {
return age >= 18;
}
}
const person = new Person('张三', 20);
person.speak(',很高兴认识你!');
function RangeValidator(min: number, max: number) {
return function (target: object, propertyKey: string, descriptor: PropertyDescriptor) {
const originalSetter = descriptor.set
descriptor.set = function (value: number) {
if (value < min || value > max) {
throw new Error(`Value must be between ${min} and ${max}`)
}
originalSetter.call(this, value)
}
}
}
class Weather {
private _temp: number
constructor(temp: number) {
this._temp = temp
}
@RangeValidator(-50, 50)
set temp(value: number) {
this._temp = value
}
get temp() {
return this._temp
}
}
const w1 = new Weather(25)
console.log(w1.temp) // 25
w1.temp = 900 // Error: Value must be between -50 and 50
// 参数装饰器,要求被装饰的参数不能是数字
function NotNumber(target: any, propertyKey: string, parameterIndex: number) {
let notNumberArr: number[] = target[`__notNumber_${propertyKey}`] || [];
notNumberArr.push(parameterIndex);
target[`__notNumber_${propertyKey}`] = notNumberArr;
}
// 方法装饰器,验证被装饰的方法的参数是否满足要求
function Validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;
descriptor.value = function (...args: any[]) {
const notNumberArr: number[] = target[`__notNumber_${propertyKey}`] || [];
for (const index of notNumberArr) {
if (typeof args[index] === 'number') {
throw new Error(`⽅法 ${propertyKey} 中索引为 ${index} 的参数不能是数字!`);
}
}
return method.apply(this, args);
};
return descriptor;
}
class Student {
name: string;
constructor(name: string) {
this.name = name;
}
@Validate
speak(@NotNumber message1: any, message2: any) {
console.log(`${this.name}想对说:${message1},${message2}`);
}
}
const s1 = new Student('张三');
s1.speak(10, 'hell0');