1 引入
1.1 介绍
javascript变量的特点是变量类型是可变的,这是一个很好的特性,但是如果你在之前写了一个处理两个数字的函数,之后你忘记了里面只能用数字,你传了字符串进去,但是js不会报错,这使得debug麻烦起来
所以typescript在js的基础上进行了扩展,限制了一些东西,ts使得书写更复杂,但是更规范起来,而且也有了更加完善的报错信息。
ts能够兼容所有的js语法,所以你可以完全用js的语法也没有问题
1.2 配置环境
需要先安装nodejs,然后用npm安装typescript
npm i -g typescript
然后你创建一个ts文件,写一个普通的console.log
然后像使用javac一样使用tsc 文件
来编译它,注意找准路径
这个时候就编译成js文件了。
这个时候有一个特点,如果你ts文件即使有ts的语法错误,tsc依旧能够编译成功。后面能够修改成有错不能编译成功。
2 类型声明
数据类型
ts的数据类型有number string boolean 字面量 any unknown void never object array tuple enum
声明
比如声明一个number类型
let a:number
如果你变量类型用错了,就会报错。
字面量类型
字面量就是一个确切的值
let a:10
这个时候a就只能是10
这种写法比较少见
联合声明
可以用|来放宽限制
let a:"male"|"female";
let b:boolean|number;
any类型
any就是所有类型,在ts中要避免使用这种情况,所有的let就相当于隐式的any
unknown类型
unknown是一个安全类型的any
如果是any,下面的代码可以成功,最后的结果是a也变成了any类型的,a的类型被改变了,ts的类型判断就没有意义了。
let b:any = 51;
let a:string;
a = b;
所以对于我们还不知道类型是什么的变量,可以先用unknown来声明
下面的代码会提示你b的变量未知,不能复制给string类型的a
let b:unknown=51;
let a:string;
a=b;
所以这个时候有三种方式来断言b是string类型的。
a = b as string;
if(typeof b === string){
a = b;
}
a = <string>b;
暂时觉得第二种更加安全.
void类型
与c的函数一样,就是没有返回类型
object类型
如果直接声明一个object类型的也没有意义,万物皆对象
可以这样设置
let a:{name: string}//
let a:{name:string, age?:number}//可选属性
let c:{name:string, [propname:string]: number}//声明后面的number类型属性
let f:(a:number, b:number)=>number;//f是一个函数对象,声明了形参类型和返回类型的
数组类型
let a:string[]
let b:number[]
let c:array<number>;
元组
元组是固定长度的数组,性能比可变长度的数组要好
let h:[string, number];
h = ["hello", 123];
enum枚举
这是ts中新增的类型
enum gender{male, female};//这里对应的值是多少不重要了,有点像是map
let i:{name: string, gender:gender};
i = {name:"xxx", gender:gender.male}
console.log(i.gender === gender.male);
类型别名
type mytype = 1|2|3|4
let k:mytype;
函数声明
下面的函数就能声明参数类型和返回类型
function sum(a:number, b:number):number{
return a+b;
}
隐式声明
a这个时候会直接被解析成bool类型
let a = false;
3 编译选项
3.1 编译多个文件
watch监视模式
每次ts修改想要得到编译后的js需要每次使用tsc命令
可以用tsc app.ts -w
开启监视模式,每次修改的时候都会自动编译了。
但是当文件多的时候很麻烦。
tsconfig.json引入
所以我们需要一个类似c里面makefile的文件来设置编译规则,让它自动编译所有的文件,并且可以设置编译成哪个版本的js,es3还是es5还是es6
先在底下创建一个tsconfig.json的文件,就可以在里面设置编译规则。
就算这个时候你json文件里面什么都不写,只写一个json的大括号,然后使用tsc,也能自动编译同目录下所有的ts文件,以及同目录下子文件ts
include
tsconfig是默认编译根目录下所有的ts文件,include是指定要编译的文件。
指定编译src底下的所有ts文件
{
"include": [
"./src/**/*"
]
}
其中**表示任意目录,*表示任意文件
exclude
指定不需要被编译的文件,一般不需要设置
有一个默认值
"exclude":["node_modules", "bower_components", "jspm_packages"]
extends
指定直接使用哪个文件里面的json
3.2 compileroptions
编译选项是配置文件中非常重要的配置选项,与include同级
target
编译版本,默认是es3,兼容性较好
"compileroptions":{
"target": "es5"
}
module
模块化规范,比如comonjs或者es6,假如不知道有哪些选项,就乱填一个让它报错也行。
"module": "es2015"//也就是es6
lib
指定使用到的库
document也用到了名为dom的库。
一般不用设置它,默认选项就足够了。
outdir
设置编译输入的位置。
"outdir": "./dist"
outfile
将编译后的js合并成一个文件。
"outfile": "./dist/app.js"
会将全局作用域中合并,用的也比较少。
allowjs
是否编译js文件,默认是false。
"allowjs":true
checkjs
是否检查js文件语法,默认也是false。
true之后就会用ts的语法来检查js文件。
removecomments
是否移除注释,默认是false
noemit
不生成编译后文件。用的少。只能用来检查语法。
noemitonerror
ts语法错误时不编译成js
下面就是语法的严格检查了。
alwaysstrict
编译后的文件默认不使用严格模式,严格模式的性能要好一点
模块化文件就是在严格模式之下了,所以如果用到了import 和export的时候不会有"use strict"
noimplicitany
不允许使用隐式的any,默认false
比如let a = 5就是一个隐式的。
noimplicitthis
不允许使用隐式的this,默认false
strictnullchecks
严格检查空值,当你用dom查询一个元素的时候,可能这个元素不存在,但是你将它赋值给一个变量了,这个变量就有可能是空值。
let test1 = document.getelementbyid("test1");
test1.addeventlistener("click", ()=>{});
可以设置
if(test1){
test1.addeventlistener("click",()=>{});
}
strict
所有严格检查的总开关
将strict设置为true就可以不设置上面的严格检查了。
建议打开。
4 webpack配置
这一步需要有webpack基础,假如是使用vue和react就不需要自己配置了。
除了安装webpack必须的webpack webpack-cli
安装打包ts的文件typescript和ts-loader
npm i webpack webpack-cli ts-loader typescript -d
module里面的配置
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
exclude: /node-modules/
}
]
}
5 类
类的用法和js中es6的类差不多。
class person{
//实例属性
name:string = "孙悟空";
readonly id:string = 123;
static sayhello():void{
console.log("hello");
}
}
构造函数
class dog(){
name:string;
age:number;
constructor(name: string, age:number){
this.name = name;
this.age = age;
}
}
const dog = new dog("131", 12);
接下来的继承 super 抽象类, 接口都和java差不多。
抽象类
抽象类只能用来继承,不能实现,也可以在里面写抽象方法。
abstract class animal{
name:string;
abstract sayhello:void;
}
class dog extends animal{
sayhello(){
console.log("31113");
}
}
接口
接口就是用来定义一个类的结构的。
type mytype = {
name: string,
age: number
};
const obj1:mytype = {
name: "ss",
age: 11,
}
interface mytype2 = {
name: string;
age: number;
}
//可以用来替代type来声明对象
const obj2:mytype2 = {
name: "3131",
age: 11,
}
//需要实现接口中的所有内容
class myclass implements mytype2{
name: string,
age: number
}
private与public
private不能被直接访问,只能在类内部使用,可以用getter和setter设置。
protected比起private还能在子类中使用。
public就没有限制了。
class person{
private _name:string;
private _age:number;
get name(){
return this._name
}
set name(value){
this._name = value;
}
}
泛型
//类型不确定的时候,用fn<t>来声明函数
functin fn<t>(a:t):t{
return a;
}
fn(10);
fn<string>("hello");
但是这样写的话限制太少了。
可以用接口给泛型加限制。
interface inter{
length: number;
}
function fn<t extends inter>(a:t):number{
return a.length;
}
这个时候a必须实现inter接口,也就是t类型的变量必须有length这个属性。
6 导入外部资源的问题
js包
如果你开启严格模式用ts导入js的包,那么会提示你包不存在。
解决方法有两个
1 正式一点的包会有专门为ts服务的包,一般是@types/包名
2 定义一个declaration.d.ts,这样写,比如我想要在ts中导入gsap包,declare module "gsap";
注意在tsconfig.json中将declaration.d.ts也纳入编译中
{
"compileroptions": {
"module": "es2015",
"target": "es2015",
"strict": true
},
"include": ["src", "declaration.d.ts"]
}
导入图片
也需要在declaration.d.ts中将各个类型的图片定义成module
declare module "gsap";
declare module "dat.gui";
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
然后就可以和模块一样导入这张图片,在需要使用图片的地方直接放入这个对象即可。
import imag2 from "../assets/textures/bg.jpg"
7 无法用方括号变量名的形式读取属性
比如下面的代码会提示你不能用any
obj[propname];
所以对于propname应该做类型声明,但是直接声明成string也不行,
可以用keyof
keyof是读取一个类型的所有属性名的关键字。
通过typeof获取obj的类型,然后通过keyof获取所有的属性名,就能将propname限制在obj的属性里面了。
get(obj, propname:keyof typeof obj) {
console.log(`有人读取了${propname}属性`);
return target[propname ];
}
发表评论