文章说明:本文章为拉钩大前端训练营所做笔记和心得,若有不当之处,还望各位指出与教导,谢谢 !
一、 TypeScript快速上手
- 安装TypeScript
yarn init --yes # 初始化项目目录
yarn add typescript --dev # 开发依赖安装到本项目上
- 新建TypeScript文件,文件名01-getting-started.ts,其文件扩展名默认为 .ts
// 可以完全按照 JavaScript 标准语法编写代码
const hello = (name: any) => {
console.log(`Hello, ${name}`)
}
hello('TypeScript')
- 使用TypeScript编译上述文件
yarn tsc 01-getting-started.ts
编译后的文件01-getting-started.js
js文件:
// 可以完全按照 JavaScript 标准语法编写代码
var hello = function (name) {
console.log("Hello," + name);
};
hello('TypeScript');
二、配置文件
ts不仅可以编译单个ts文件,还可以编译整个项目或工程。在编译整个项目前,我们需要先给整个项目创建一个TypeScript的配置文件。
使用命令自动生成配置文件:
yarn tsc --init
在根目录下会多出tsconfig.json文件
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es2015", /* 设置编译后的JavaScript所采用的ECMAScript,在代码当中把所有的新特性都标准都会转换成es2015标准的代码Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /*指的是我们输出的代码会采用什么样的方式去进行模块化 Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "dist", /* Redirect output structure to the directory. */
"rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
三、原始类型
/*
原始类型
*/
const a:string = 'foobar'
const b:number = 100 //数字类型还包括NaN infinity
const c:boolean = true //false
//以上类型在非严格模式(strictNullChecks)下还可以赋值null
// const d:boolean = null
const e:void = undefined //一般为空值,函数没有返回值的时候标记返回值类型,只能够存放null 或者 undefined,严格模式下只能是undefined
const f:null = null
const g:undefined = undefined
四、TypeScript标准库声明
- symbol 是JavaScript内置的标准对象,例如Array、Object这些,Symbol是es2015新增的内置对象,自身也是有类型,这些类型在TypeScript当中已经帮我们定义好了
const h:symbol = Symbol()
const error:string = 100
- Array 在es5里面声明了,tsconfig.json里面的target里面定义为es5,所以说用es2015的Symbol会报错,要么修改target里为es2015,如果必须编译到es5的话,可以使用lib选项去指定我们所引用的标准库,lib里面增加es2015
Array
- "lib": ["ES2015", "DOM"],TypeScript把BOM和DOM都归结到一个标准库中,称之为DOM
-
标准库就是内置对象所对应的声明,在代码当中使用内置对象就必须要引用对应的标准库,否则TypeScript就找不到对应的类型,就会报错
五、TypeScript设置中文消息错误提示
控制台中TypeScript中文错误提示设置:
yarn tsc --local zh-CN
- vscode中文错误提示:settings里面搜索TypeScript local 设为zh-cn,但是不建议设为中文,有些不清楚错误用中文google搜索会更准确
六、TypeScript作用域问题
不同的文件里面会有相同的变量名的情况,此时就会报错:变量名定义重复。为了避免这个问题,我们会将相同的变量放到不同的作用域当中:
//放到立即执行的函数当中
(function(){
const a =123
})()
用expert {} 导出,从而该文件变成了模块,模块拥有自己的作用域
export {}
七、Object类型
export {} //确保跟其它示例没有成员冲突
const foo:object = function(){}//object 可以接受对象,数组,函数,并不单指普通的对象
const obj:{foo:number,bar:string} = {foo:123,bar:'string'}//这是字面量对象的定义,其中里面的属性与属性类型必须一致,不能多也不能少,不过更专业的方式是用接口
八、数组类型
//数组类型
//两种定义方式,全都是数字类型数组
const arr1:Array<number> = [1,2,3]
const arr2:number[] = [1,2,3]
//======================================
function sum(...args:number[]){
//判断是不是每个成员都是数字,而现在typeScript只需要加入数字的注解
return args.reduce((prev,current) => prev+current,0)
}
九、元祖类型
元祖(Tuple)是一个明确元素数量以及每一个类型的数组,各个元素的类型他不必要完全相同,可以用类似数组字面量的做法定义元祖类型
export {} //确保跟其它示例没有成员冲突
const tuple:[number,string] = [18,'string']
// const age = tuple[0]
// const name = tuple[1]
const [age,name] = tuple
//---------------------------------
const aaa:[string,number][] = Object.entries({
foo:123,
bar:456
})//获取对象当中的键值数组,其中的每一个键值就是元祖
const [key,value] = aaa[0]
十、枚举
在开发过程中经常要用到某几个数值代表某种状态,枚举便产生了,首先用常量代替枚举:
const PostStatus = {
Draft:0,
Unpublished:1,
Published:2
}
const post = {
title:'Hello TypeScript',
content:'TypeScript is a typed superset of JavaScript',
status:PostStatus.Draft//取出对象中属性对应的值
}
枚举:
enum PostStatus {
Draft = 0,
Unpublished = 1,
Published=2
}
数字枚举:
enum PostStatus {//若是不赋值的话则从0开始累加,若是第一个枚举值赋了值,则后续的在第一个基础之上累加
Draft = 6,
Unpublished,
Published
}
字符串枚举:
enum PostStatus {//除了是数字以外还可以是字符串,需要给每一个枚举值赋予字符串值
Draft = 'aaa',
Unpublished = 'bbb',
Published ='ccc'
}
枚举类型它会入侵到我们运行时的代码,它会影响我们编译后的结果,它最终会编译为双向的键值对对象,就是可以通过键取值,也可以通过值获取键。若代码当中不会使用索引器的方式去访问枚举则建议使用这个常量枚举
const enum PostStatus {//若代码当中不会使用索引器的方式去访问枚举则建议使用这个常量枚举
Draft,
Unpublished,
Published
}
十一、函数类型
函数声明式,参数值为number,函数返回值为string,在参数后面添加问号变成可选参数或者也可以使用es2015中参数默认值的特性,赋予b默认值,这样也变成了可选参数,若想接收任意类型的参数则用...rest
export {} //确保跟其他示例没有成员冲突
function func(a?:number,b:number = 10,...rest:number[]):string{// 参数值为number,函数返回值为string,在参数后面添加问号变成可选参数或者也可以使用es2015中参数默认值的特性,赋予b默认值,
//这样也变成了可选参数,若想接收任意类型的参数则用...rest
return 'func1'
}
func(100,200)//参数个数也必须相同
函数表达式
const func2:(a:number,b:number) => string = function(a:number,b:number):string{
return 'func'
}
十二、任意类型(弱类型)
export {} // 确保跟其它示例没有成员冲突
function stringfy(value:any){//any类型不会有类型检查,轻易不要使用
return JSON.stringify(value)
}
stringfy('string')
stringfy(100)
stringfy(true)
十三、隐式类型推断
在TypeScript中,如果我们没有通过类型注解取标记一个变量的类型,TypeScript会根据变量的使用情况去推断变量的类型。这种特性称为隐式类型推断
export {} //确保跟其它示例没有成员冲突
let age = 18 //TypeScript 推断为number类型
// age = 'string'//此时会类型报错
let foo //TypeScript 推断为任意类型any
foo = 100
foo = 'string'
-
建议为每个变量添加明确的类型
十四、类型断言
特殊情况下,TypeScript无法推断出变量的具体类型,开发者根据代码的使用情况,总是根据使用情况可以知道变量是什么类型的。
//假定这个nums来自一个明确的接口
const nums = [110,120,119,112]
const res = nums.find(i => i>0)//TypeScript此时推断返回值为number或undefined,可能会不存在大于0的数字
const num1 = res as number //开发者告诉TypeScript断言res为number类型,此时并不是类型转换
//类型转换是运行时的概念,而类型断言是编译时的概念,编译过后断言就不存在了
const num2 = <number>res //JSX 下不能使用
十五、接口
它可以去用来约定对象的结构,当去使用一个接口时就必须要遵循这个接口全部的约定,最直观的体现就是约定一个对象当中具体应该有哪些成员,而且成员的类型是什么样的
interface Post{
title:string
content:string
}
function printPost(post:Post){//显示的要求传入的对象必须要有title和content成员
console.log(post.title)
console.log(post.content)
}
printPost({
title:'Hello TypeScript',
content:'A javascript superset'
})
接口就是用来约束对象的结构,一个对象去实现一个接口,就必须要去拥有这个接口当中所约束的所有成员。只是对对象做一个约束,实际运行阶段并没有意义.
- 可选类型
interface Post {
title:string
content:string
subtitle?:string//可有可无
}
- 只读类型
interface Post {
title:string
content:string
readonly summary:string //只读选项,重新修改这个值会报错
}
const hello:Post = {
title:'Hello TypeScript',
content:'A javaScript superset',
summary:'A javascript' //修改这个值会报错
}
hello.summary = 'other'//报错
- 动态类型
interface Cache {
[prop:string]:string//键和值的类型都是string
}
const cache:Cache = {}
cache.foo = 'value1'//在cache对象上动态的添加任意成员,只不过这些成员都必须是string类型的键值
cache.bar = 'value2'
十六、类
类:描述一类具体事物的抽象特征,描述一类具体对象的抽象成员,ES6开始JavaScript中有了专门的class,TypeScript增强了class的相关语法
export {}
class Person {//类的属性在使用之前必须要在类型当中声明,就是为了给我们的属性做一些类型的标注,除此之外,我们仍然可以按照es6标准当中的语法为这个类型去声明一些方法
name:string //= 'init name'
age:number
constructor(name:string,age:number){
this.name = name
this.age = age
}
say(msg:string):void{
console.log(`I am ${this.name},${msg}`)
}
}
十七、类的修饰符
export {}
class Person {//
public name:string //= 'init name' //公有成员,默认为public加不加public都是一样的,建议加上
private age:number//私有属性,只能够在类的内部去访问
protected gender:boolean //外部无法访问
constructor(name:string,age:number){
this.name = name
this.age = age
this.gender = true
}
say(msg:string):void{
console.log(`I am ${this.name},${msg}`)
console.log(this.age)
}
}
class Student extends Person{
constructor(name:string,age:number){
super(name,age)
console.log(this.gender)//可以访问父类当中的gender属性,protect指的是只允许在子类当中去访问对应的成员
}
}
const tom = new Person('tom',18)
console.log(tom.name)
// console.log(tom.age)//标为私有属性无法访问
// console.log(tom.gender)//外部访问出错
构造方法的修饰符
class Student extends Person{
private constructor(name:string,age:number){//若是变成private,则这个类型就无法实例化了,也不能够被继承
super(name,age)
console.log(this.gender)
}
static create (name:string,age:number){
return new Student(name,age)//使用类的方式创建这个类的实例,create方法还是在这个类的内部还是能够调用到这个类的构造函数的
}
}
// const jack = new Student()//无法实例化,只能够在这个类的内部添加一个静态方法,然后在这个方法当中去创建这个类型的实例,因为private只允许在内部访问
const jack = Student.create('jack',18)
十八、类的只读属性
export{}
class Person {//
public name:string //
private age:number//
protected readonly gender:boolean //如果这个属性已经有访问修饰符的话,readonly要放在访问修饰符的后面,对于只读属性可以选择在类型声明的时候直接通过等号的方式去初始化,
//也可以在构造函数当中去初始化,两者只能选其一,初始化过后这个属性不能够再修改了,不管是内部还是外部
constructor(name:string,age:number){
this.name = name
this.age = age
this.gender = true
}
say(msg:string):void{
console.log(`I am ${this.name},${msg}`)
console.log(this.age)
}
}
const tom = new Person('tom',18)
console.log(tom.name)
// tom.gender = false
十九、类与接口
-
类与类的公共的特征会用接口抽象出来
export {}
//用接口约束两个类之间公共的能力
//最好是一个接口约束一个能力,一个类去实现多个接口
interface Eat{
eat(food:string):void// 不作具体的方法的实现
}
interface Run{
run(distance:number):void
}
class Person implements Eat,Run{//用implements实现EatAndRun接口,必须要有接口中的成员
eat(food:string):void{
console.log(`优雅的进餐:${food}`)
}
run(distance:number){
console.log(`直立行走:${distance}`)
}
}
class Animal implements Eat,Run{
eat(food:string):void{
console.log(`呼噜呼噜的吃:${food}`)
}
run(distance:number){
console.log(`爬行:${distance}`)
}
}
十九、抽象类
抽象类跟接口有点类似,它也是可以用来约束子类当中必须要有某一个成员,不同于接口的是抽象类可以包含一些具体的实现而接口只能够是一个成员的一个抽象不包含具体的实现,比较大的类目建议使用抽象类
export {}
abstract class Animal { //定义为抽象类之后,只能够被继承而不能new的方式创建对应的实例对象
eat(food:string):void{
console.log(`呼噜呼噜的吃:${food}`)
}
abstract run(distance:number):void //抽象方法,也是不需要具体实现
}
class Dog extends Animal {
run(distance: number): void {
console.log('四角爬行',distance)
}
}
const d = new Dog()
d.eat('狗粮')
d.run(100)
二十、泛型
泛型指定义函数接口或类的时候,我们没有去指定具体的类型等到我们使用的时候再去指定具体类型的这样一种特征,以函数为例,泛型就是声明这个函数时我们不去指定具体的类型,等到我们再调用它的时候再去传递一个具体的类型,是为了极大程度的复用我们的代码
export {}
// 创建一个数字类型数组
function createNumberArray(length:number,value:number): number[]{
const arr = Array<number>(length).fill(value)//通过Array对象创建一个指定长度的空数组,在通过fill方法填充每一个值,Array对象默认创建的是any类型数组,
//所以要指定下里面的类型,用泛型参数<number> 即指定为数字类型的数组
return arr
}
//创建一个string类型数组
function createStringArray(length:number,value:string): string[]{
const arr = Array<string>(length).fill(value)
return arr
}
function createArray<T>(length:number,value:T): T[]{//在函数名后面使用一对尖括号,在尖括号里面定义泛型参数,一般用T表示,然后把函数当中不明确的类型用T表示
const arr = Array<T>(length).fill(value)
return arr
}
const res = createArray<string>(3,'foo')//创建一个string类型数组
总之,泛型就是把我们定义时不能够明确的类型变成一个参数,让我在使用的时候再传递这样的一个类型参数
二十一、类型声明
类型声明:说白了就是一个成员在定义的时候因为种种原因没有声明一个明确的类型,然后在使用的时候在单独为他再做出一个明确声明,这样做是为了考虑兼容一些普通的js模块
yarn add lodash 安装lodash模块
import {camelCase} from 'loadsh'//导入这个模块时,报错,会让你安装一个类型声明模块
declare function camelCase(input:string): string //有了这样的类型声明过后,再使用这个函数就会有对应的类型限制
//将字符串转化为驼峰模式
const res = camelCase('hello typed')
在typeScript当中引用第三方模块,如果这个模块当中不包含所对应的类型声明文件,则尝试去安装一个所对应的类型声明模块,若是没有这样的一个类型声明模块,只能够使用declare语句声明,安装lodash类型声明模块:yarn add @types/lodash --dev
TypeScript就先了解到这里,更多TypeScript内容以后再分享,谢谢!