ts的优缺点
优点:
- 增强代码的可读性和可维护性
- 在编译时即可发现大部分的错误
- 强类型
缺点:
- 短时间内会增加工作量
环境搭建
安装
npm install -g typescript
可在cmd输入tsc 来查看是否安装成功,安装成功则有反应
可通过tsc +ts文件名将ts文件编译成js文件,然后通过node js文件运行,也可以
npm install -g ts-node
全局安装ts-node,然后ts-node ts文件 运行
ts常见语法
let num: number = 10; // 不能给其他类型的数据,因为已经定义变量未数字类型
let str: string = 'hello world';
let bool: boolean = true;
let udf: undefined = undefined;
let nl: null = undefined;
// ...其他类型依次类推
const arr : string[] = ['小红', '小明']; // 定义了字符串类型的数组,里面的元素只能是字符串
const arrPlus : [string, number, string] = ['hello', 2, 'world']; // 支持多种类型,但是数组必须按照所示类型
const multipleTypesArr : (string | number)[] = ['hello', 'world', 123] // 可以定义多个类型的数组
interface person {
name: string,
age: number
}
// 定义了对象,里面包含字符串类型的name和数字类型的age
const XIAO_MING: person = {
name: '小明',
age: 18
}
const func: () => boolean = () => { // 定义了一个函数,必须呀返回一个布尔值
return true
}
const voidFunc = () : void => { // 函数没有返回值
console.log('hello');
}
const nevenFunc = () : never => { // 函数永远不会执行完(抛出异常,死循环等)
throw new Error();
console.log('hello');
}
const addNumber = ({ num1, num2 }: { num1: number, num2: number }) => { // 解构赋值写法
return num1 + num2;
}
ts类型推断和类型注解
// 当ts可以推断出数据类型的时候(类型推断),就不用限制类型了,否则必须要加类型,类似于上面那种形式(类型注解)
const one = 1;
const two = 2;
const addTotal = (one, two) => { // 这里推断one和two是any
return one + two;
}
const total = addTotal(1, 2);
// 但是为了防止有时将函数写成这样
const addTotal = (one, two) => { // 这里推断one和two是any
return one + two + ''; // 这个时候返回的结果是string类型
}
const total = addTotal(1, 2); //自然也是string类型,但是我们实际需要的是number类型,怎么办呢?
const total: number = addTotal(1, 2); // 因为定义的类型和接收到的类型不一样,所以会报错,但是不直观,应该在源头做限制
// 应该写成这样
const addTotal = (one, two): number => { // 这里推断one和two是any
return one + two; // 这个时候如果后面加上字符串的话会报错
}
const total = addTotal(1, 2);
类型别名
type Man = {
name: string,
age: number
}
const man: Man= {
name: '张三',
age: 18
}
// 也可以这样写
class Man {
name: string;
age: number;
}
const gentleman: Man[]= [{
name: '张三',
age: 18
}]
接口(interface)
// 接口的优势,在于可以重复调用,和type的区别:type既可以像interface一样,也可以type Man = string;,而interface只能时像对象一样的格式
interface Women {
name: string;
age: number;
money ?: number; // ?:代表可选
[propname :string]: any; // 可以接受字符串类型的key,任意类型的value
say(): string // 必须有一个say方法,返回值时string
}
function a(_: Man) {}
function b(_: Man) {}
function girlFunc(girl: Women):void {
console.log(girl.name)
console.log(girl.age)
console.log(girl.money)
console.log(girl.husband)
girl.say();
}
girlFunc({
name: '小红',
age: 20,
money: 100000,
husband: '小明',
say() {
console.log('hello world');
return 'say'
}
})
// interface可以限制对象,类和interface
class ClassA implements Women {
name = '小红';
age = 20;
money = 100;
husband = '小明';
say() {
return 'say'
}
}
interface Teacher extends Women {
teach(): string
}
function teacherFunc(girl: Teacher):void {
console.log(girl.name)
console.log(girl.age)
console.log(girl.money)
console.log(girl.husband)
girl.say();
girl.teach()
}
teacherFunc({
name: '小红',
age: 20,
money: 100000,
husband: '小明',
say() {
console.log('hello world');
return 'say'
},
teach() {
return 'teacher'
}
})
类的使用
class Hero {
content = '拯救';
say() {
return 'say'
}
}
const hero = new Hero();
console.log(hero.say())
class HeroChild extends Hero {
// 对say方法的重写
say() {
return 'child say'
}
sayHello() {
// 使用super可以继续使用父类的方法
return super.say() + 'hello world'
}
sayAny() {
return 'any'
}
}
const child = new HeroChild();
console.log(child.say())
console.log(child.sayAny())
console.log(child.sayHello())
// 类的访问类型
class Student {
public name: string; // 访问权限是公开的,默认是public
private score: number; // 只能在内部访问
protected id: number; // 只有继承的子类可以访问
}
const std = new Student();
std.name = '张三';
// std.score = 100; // 报错,因为是private权限
// std.id = 1 // 报错
class ClassAStudent extends Student {
say() {
console.log(this.id)
}
}
const classAStd = new ClassAStudent();
classAStd.say()
// classAStd.id = 10; // 报错,因为只能在继承的类中使用
// 类的构造函数
class A {
name: string;
constructor(name: string) {
this.name = name;
}
// 以上代码可简写成 constructor(public name: string) {},可代替上面四行代码
}
const newA = new A('张三');
console.log(newA.name)
class B extends A {
public age: number
constructor(name: string, age: number) {
super(name) // 一定要写,因为A的构造函数有参数,如果没有参数,需要super()
this.age = age
}
}
const newb = new B('李四', 18)
console.log(newb.name);
console.log(newb.age)
// getter,setter, static
class C {
age: number;
set cAge(age: number) {
this.age = age;
}
get cAage() {
return this.age - 10; // 输出的时候对值进行修改
}
static say() {
console.log('hello')
}
}
const c = new C();
c.age = 35;
console.log(c.age)
console.log(C.say()) // 可直接通过类调用
类的私有变量和抽象类
abstract class abstractClass {
abstract skill() // 抽象类中的抽象方法,不用对抽象方法进行描述,但是所有继承的子类都必须要实现这个方法
}
class implementClass extends abstractClass {
public readonly _name: string; // 只读属性需要前面加上readonly,一般前面加_代表私有变量
skill() {
console.log('say hello');
}
}
tsconfig.json
tsc --init
通过上述指令,可以创建一个tsconfig.json文件,可执行以下指令
tsc // 将当前所有ts文件按照配置文件编译成js文件
tsc xx.ts // 只将ts文件编译成js文件,不会按照配置文件编译
tsconfig 内容
{
...
"include": ["xx.ts", "yy.ts"] // 再次执行tsc的时候,只会编译数组中的文件
“exclude”: ["xx.ts", "zz.ts"] // 除了数组中的ts文件都会编译
“files”: [] // 效果和include一样
// "strict": true, 是否严格按照ts规范,只有注释掉下面配置项才起用
...
"noImplicitAny": false, // any类型是否需要书写,为false时可以省略不写
"strictNullChecks": true, // 是否严格检查null,为false时,可以执行类似以下代码 const name: string = null
"sourceMap": true, // 是否生成ts和js之间的关系和信息,比如编译后的js文件名时1.js,那么就会生成1.js.map
"rootDir": "./", // 要编译的路径
"outDir": "./public", // 要打包的路径
"noUnusedLocals": true, // 为false时,可以允许变量定义之后不使用
"noUnusedParameters": true,// 为false时,可以允许函数定义之后不使用
...
...
}
联合类型和类型保护
interface AnimalA {
isAnimalA: boolean
eat: () => {}
}
interface AnimalB {
isAnimalB: boolean
sleep: () => {}
}
function handleFunc(animal: AnimalA | AnimalB ) { // 接受的参数类型为两种接口中的任意一种
// animal.eat() // 会报错,因为animal也可能时AnimalB类型的数据,是没有eat方法的
// 可以使用以下方法调用
if ('eat' in animal) {
animal.eat();
} else {
animal.sleep();
}
}
function add(num1: string | number, num2: string | number) {
// return num1 + num2; // 会报错,因为string不能和number相加
if (typeof num1 === 'string' || typeof num2 === 'string') {
return `${num1} + ${num2}`;
}
return num1 + num2;
}
class C {
count: number
}
function addNum(first: object | C, second: object | C) {
// return first.count + second.count; // 会报错,因为任意类型的object不一定会有count
if (first instanceof C && second instanceof C) {
return first.count + second.count;
}
return 0;
}
#Enum 枚举类型
enum State { // 下标默认是从0开始
open, // 0
close // 1
}
enum NewState { // 因为有修改默认值,所以是以10开始
open = 10, // 10
close // 11
}
function getState(state: number) {
if (state === 0) {
console.log('open')
} else {
console.log('close');
}
}
getState(0);
getState(State.open)
getState(State.close);
console.log(State[1]) // 可以直接得出key,=> close,同样State[0]等于open
泛型
泛型在函数中的使用
// 简单形式
function getNum<T>(num1: T, num2: T) { // 泛型一般使用T来代表,但是不限定时T,可以起任意名字
return `${num1} + ${num2}`;
}
getNum<number>(1, 2);
// 数组形式
function getArray<T>(arr: T []) { // T[] 也可以写成Array<T>
return arr;
}
getArray<string>(['1', '2']) // <>中写入类型,泛型不关心函数定义时的类型,而是关心调用的时候的类型,<>里面的类型就是T的类型
// 多个泛型
function getString<T, S> (str: T, num: S) {
// ...
}
getString<string, number>('hello', 1)
泛型在类中的使用
// 简单形式
class TClass<T> {
constructor(private users: T[]) {}
getUser(index: number): T {
return this.users[index]
}
}
const users = new TClass<string>(['张三', "李四"])
console.log(users.getUser(0))
// 规定泛型中有某个字段
interface U {
name: string
}
class TClass<T extends U> { // 泛型必须继承自U,必须有name字段
constructor(private users: T[]) {}
getUser(index: number): string {
return this.users[index].name
}
}
const users = new TClass([{ name: '张三' }, { name: '李四' }]) // 需要传对象,并且有name字段
console.log(users.getUser(0))
// 泛型的约束
interface U {
name: string
}
class TClass<T extends string | number> { // 泛型必须时string或者number类型
constructor(private users: T[]) {}
getUser(index: number): string | number {
return this.users[index]
}
}
const users = new TClass(['1', 2]) // 需要传对象,并且有name字段
console.log(users.getUser(0))
命名空间
// 命名空间是为了防止变量冲突而设立的
namespace Page {
class A {};
class B {};
class C {};
export class All { // 需要导出就用export
constructor() {
new A();
new B();
new C()
}
}
}
这个时候就可以在另外一个文件直接使用(不需要import)
const all = new All()
感谢b站技术胖的视频,原视频地址:https://www.bilibili.com/video/BV1qV41167VD?p=1