良好编码习惯养成助手——TS

js简单易上手灵活多变,既是优点,也是缺点,会为上线的代码埋下隐患,简单举“栗”子:

let a;
a = 10;
a = '这是新值';

在js中这样书写是不会报错的,因为js不会对函数的参数类型和个数进行检测,但在项目中如果我们所需的值仅只为数字进行运算如下:

function sum(a, b){
    
    
	return a + b;
}
console.log(sum(12, 34)) // 46
console.log(sum(12, '34')) // 1234

显然无论我们传数字还是字符串,编辑和测试都不会出现编译错误,但是返回的数据结果却大不一样,这就导致我们在开发大型项目的过程中维护起来非常的头痛。而TS就是在js的基础上增加了类型的判断,所以一切支持js的地方都可以用ts,但值得注意的是,ts是不可直接被解析的,需要转换成js后才可以被解析。可能会有同学问为什么不直接整一套可解析的ts,是因为js就目前而言是不可代替的一门开发语言,在短时间之内添加一套辅助工具是最为这种的办法,也是为了兼容习惯了js的老程序猿。目前而言,ts不是必要的,但近几年一定会全面推广,所以快点学起来吧!
首先我们在正式使用ts之前,需要先配置好环境,如图:
ts配置流程图
首先我们先来认识一下TS基础类型

  • any => 任意类型,相当于放弃了类型校验,没有了ts的使用意义,不推荐使用
  • number => 数字类型
  • string => 字符串类型,包括字符串和模板字符串
  • boolean => 布尔类型,true 、false
  • [] => 数组类型(number[]、string[]、泛型: Array、Array)
  • [string,number,boolean] => 元组类型,必须一一对应
  • enum => 枚举类型
  • void => 表示方法返回空
  • null => 表示值缺失,空对象引用
  • undefined => 未定义
  • never => 其它类型,代表从不会出现的值
let a: number; // 当a的类型被设定为数字之后,那么a的值便只能是数字类型
a = 10;
// a = '字符串类型值'; // 此时a会报错
let a: string = '字符串类型值'; // 声明完变量直接对其进行赋值
let b = false; // 如果变量的声明和赋值是同步的,TS可以自动对变量的类型进行检测
// b = 123; // 此时b会报错,上方类型默认为b: boolean;

在书写方面呢,就像c++语言里的字面量方式,也可以给一个变量以多种类型,专业点讲叫联合类型,具体如下:

// 字面量赋值方式(字面量方式更类似于常量,只能赋值一次)
let a: 10;
a = 10;
// a = 11; // 此时a会报错,只能被赋值以10

let b: 'male' | 'female';
b = 'male'
b = 'female'
// 同理可得 => 
let c: number | boolean; // 联合类型
c = 123;
c = false;

如果当我们没有给变量声明类型时,就会有隐式类型any,由于any类型想当与放弃了类型校验,所以既然我们要用ts,就最好不要用any类型。

let a = 10;
// a = '字符串类型值'; // 自动校验a的类型为number
let b;
b = false;
b = 123;
b = '345'; // 自动校验b的类型为any(隐式类型any)

但是如果我这里有一个变量我也不知道未来会具体给他什么样的值,那就用unknown,我们可以对比来看一下:

let a: string;
let b: any;
let c: unknown;
// any与unknown均可以赋值任意类型都不会报错,但他们的区别就在于any赋值给已有类型的变量不会报错,而unknown会作为一种未知类型而被限制
a = b;
c = b;
// a = c; // 因为a为string,c为unknown而类型校验不通过
b = 122 | '345' | false; // 各种类型赋值均不报错
c = 122 | '345' | false; // 各种类型赋值均不报错


// unknown实际上是一个类型安全的any,不能直接赋值给其它变量,如果要赋值,则需先类型一致
// 方法一:
if(typeof c === "string") {
    
    
	a = c
}
// 方法二:类型断言
a = c as string; // 断言的两种书写方式
a = <string>c;

接下来,我们说一下函数的类型:

// 返回数字类型
function fun(): number {
    
    
	return 123;
}
// 返回多种类型
function fun(a: boolean): string | number {
    
    
	if(a) {
    
    
		return 521;
	} else{
    
    
		return '你失去了我';
	}
}
fun(true);
// 返回空类型
function fun(): void {
    
    
	console.log('浩浩爱茜茜');
	// return ;
	// return undefined;
	// return null;
}
// 没有返回值,报错的时候
function fun(): never{
    
    
	throw new Error('这里有一个错误');
}

说完了函数返回值我们再来说一下函数的参数:

// 设置函数结构的类型声明 (参数:类型)=> 返回值类型
let f = (a:number,b:number)=> number;
f = function fun(c1:number,c2:number): number{
    
    
	return 10;
}

有一种我们不会用到的类型那就是object,为什么这么说呢,在我们的js当中呢,花括号是一个对象,函数是一个对象…万物皆对象,所以在实际应用中,object并不会用到:

let a = {
    
    }; // 声明a是一个对象类型,并且可以指定对象中有哪些属性
let b = {
    
    name: string, age: number};
b = {
    
    name: '茜茜',age: 3};
// b = {naem: '浩浩', age: 3, mex: '男'}; // 属性只能一一对应
// 可以通过加?将属性变为非必要属性
let c = {
    
    name: string, age: number, mex?: string};
c = {
    
    name: '茜茜',age: 3};
c = {
    
    name: '浩浩', age: 3, mex: '男'}

// 在开发过程中呢还可能存在的情况是对象中的属性是未知的,比如参数的不确定性,数量不确定,类型不确定,这种情况下则需要添加[propName: string]: any
let d = {
    
    name: string, [propName: string]: any}; // propName是自定义的,非固定
d = {
    
    name: '茜茜'};
d = {
    
    name: '浩浩', age: 3, mex: '男', text:'我的老婆是茜茜'}
// d = {age: 9, mex: '女', text:'我是无名氏'} // 缺少了必要属性name,所以报错

说完了对象,那就不得不说一下数组,上干货:

// 类型[] 或者 Array<类型>
let arr = number[];
arr = [1,2,3]
let arr1 = string[];
arr1 = ['1','2','3']
// tuple 元组 固定长度的数组
let arr2 = [string,string,number]
arr2 = ['1','2',3]

类似元组,我们再来看下枚举:

// enum 枚举 常用于选择范围有限的内容
enum Gender{
    
    
	male = 0, // 0->男
	female = 1 // 1->女
}
let i = {
    
    name: string,sex: Gender}; // 性别 男|女
i = {
    
    
	name: '茜茜',
	sex: Gender.female
}
console.log(i.sex === Gender.female) // true

上面我们有说到"|"的用法,那么现在来说一下’&'的使用:

// 在react语法中呢, || 表示或  &&  表示与 对应的在ts中用单一的 | + &
// let a: string & number // 实质上这种写法是不存在的,因为没有哪一种类型可以既是字符串又是数字
let obj: {
    
    name: string} & {
    
    age: number};
// obj = {name: '浩浩'} // 因为&表示同时满足所以单写一个属性会报错
obj = {
    
    name: '浩浩', age: 3}
// 那为什么不直接写let obj: {name: string,age:number}呢?这个&看上去似乎是多此一举,但是在大型的项目开发中会存在这种场景,两组数据有公共的部分,但是数据b包含了数据a的全部属性,这种情况下,我们就可以通过这种方式对已声明的a类型进行补充后使用

现在,我们来说下类型的别名使用:

// 我们要使用到type关键字
type myType = 1 | 2 | 3 | 4 | 5;
let a: myType
let b: myType
//b = 6 //因为myType里不包含6所以会报错
//类型别名呢,就相当于是一个类型的“公共组件”,用于声明公共的类型,可多处复用

与别名相类似的呢,还有接口:

// 接口主要是用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
// 同时也可以当成类型声明去使用
// -> interface
type myType{
    
     // 描述一个对象的类型
	name: string,
	age: number
}
// type myType{ // 用type来声明对象类型如果重名是会报错的
// 	sex: string,
// }
interface myInterface {
    
    
	name: string,
	age: number
}
interface myInterface {
    
     // 而接口是可以重名定义
	sex: string
}
let obj={
    
     // 此时数据内容必须满足以上接口定义的全部结构
	name: '茜茜',
	age: 3,
	sex: '女'
}

// 接口可以在定义类的时候去限制类的结构
// 接口中所有的属性不能有实际的值,只定义对象而不考虑实际值
// 接口中所有的方法都是抽象方法,定义类时去实现一个接口的要求规范

interface myInter{
    
    
	name: string,
	fun(): void
}
class MyClass implements myInter{
    
    
	name: string;
	constructor(name:string){
    
    
		this.name = name;
	}
	fun(){
    
    
		console.log('这里是一个函数')
	}
}

对象的属性可以被任意修改,这导致了数据的不安全系数增高,例如:age:-38,人的年龄是不会出现负数,为了避免这种情况,就需要用到属性封装,这其中包含了ts的修饰符的知识点:

// public 修饰的属性可以在任意位置访问或修改 默认值 (你爸的钱,你可以继承花销,你儿子,你孙子都可以)
// private 私有属性,私有属性只能在类内部使用或修改(你爸的媳妇,你不能叫媳妇)但是可以通过添加方法使得私有属性可以被外部访问(但你可以叫妈)
// protected 受保护的属性,只能在当前类和子类中访问或修改,实例中不可以访问(基因里面的东西无法通过外界来更改,只有基因内部的突变)

class Person{
    
    
	name: string;
	age: number;
	
	constructor(name:string,age:number){
    
    
		private this._name=name;
		private this._age=age;
	}
	//TS中属性存储器
	get name(){
    
    
		return this._name;
	}
	set name(val: string){
    
    
		this._name=val;
	}
	get age(){
    
    
		return this.age;
	}
	set age(val: string){
    
    
		if(val>=0){
    
     // 通过函数方法来改变就可以增加对数据的限制
			this._age=val;
		}
		else {
    
    
			console.log('输入错误,请重新输入')
		}
	}
}
const pre=new Person(name:'浩浩',age: 4)
pre.age = 3
console.log(pre) // {name:'浩浩',age:3}

最后,我们来说一说泛型:

// 在定义函数或类时,会遇到类型不明确就可以使用泛型
function fun<T>(a:T):T{
    
    
	return a;
}
// 可以直接调用具有泛型的函数
let r=fun(10)// 不指定泛型,ts可以自动对类型进行推断
let r2=fun<string>('这是一串字符串') // 指定泛型

// 泛型可以同时指定多个
function fun2<T,K>(a:T, b:K):T{
    
    
	console.log(b);
	return a;
}
fun2<number,string>(123,'345')

// 泛型加接口约束
interface Inter{
    
    
	length: number;
}
function fun3<T extends Inter>(a:T): number{
    
    
	return a.length
}
class myClass<T>{
    
    
	name:T;
	constructor(name:T){
    
    
		this.name= name
	}
}
const MC = new myClass<string>('123')

基础内容告一段落,我们在这里说一下ts的编译指令

由于ts无法在浏览器里解析,所以我们没回书写完ts代码需要用tsc 文件名.ts 进行内容的转js,这就使得非常繁琐枯燥,为了更直观的查看我们编写的效果,可以使用tsc 文件名.ts -w来监听我们ts文件的编译,但只会对当前文件进行监听;这就显得很鸡肋,如果要自动监听全部的ts文件,则需要先使用tsc --init生成一个tsconfig.json的配置文件,再使用tsc -w即可监视所有文件
这里我们再说一下tsconfig.json文件内部配置的指令:

// 这里是tsconfig.json文件内部
/*
include用来指定那些ts文件需要被编译 ** -> 任意路径 * -> 任意文件
exclude用来指定那些ts文件不需要被编译 默认值是["node_modules","bower_components","jspm_packages"],仅为默认值时可以直接忽略exclude
*/
{
    
    
	"include": [
		'./src/**/*',
		'其它任意路径'
	]
	"exclude":[
		
	],
	"compilerOptions": {
    
     //重点 编译器的配置选项
		"target": "ES3", //ts被编译的es版本 esnext(最新版本)
		"module": "commonjs", //要使用的模块化规范
		"lib": [], // 指定项目中要用到的库 eg:["dom"]
		"outDir": "./",  // 指定编译后的文件输出所在目录
		"outFile": "./",  // 将编译后的代码合并到一个文件里 这里要注意的是如果代码里有模块化引入文件的内容需要对module配置对应的规范才能进行正常的编译
		"allowJs": true,  // 是否对js文件进行编译
		"checkJs": true,   // 是否检查js代码符合规范
		"removeComments": true, // 是否移除注释
		"noEmit": true, // 不生成编译后的文件
		"noEmitOnError": true, // 当发生错误时候不生成编译后的文件
		"strict": true, // 所有严格模式的总开关
		"alwaysStrict": true, // 用来设置编译后的文件是否采用严格模式
		"noImplicitAny": true,  // 禁止隐式的any类型
		"noImplicitThis": true,  // 禁止不明确类型的this
		"strictNullChecks": true, // 禁止空值
	}
}

生成package.json文件使用命令 npm init -y

猜你喜欢

转载自blog.csdn.net/vh_YUNYANGYUMO/article/details/115213314