ts基础教程(一)
- ts基础教程
- 安装和环境搭建
- 基础类型
- 任意值
- 数组
- 元组
- 枚举
- 对象
- 参考链接
ts基础教程
安装和环境搭建
1. 全局安装typescript
npm install -g typescript
2. 项目初始化
一般而言,ts都是编译成js后再执行js,如果想一步到位,可以安装ts-node
- 新建目录(项目名可自定义):mkdir ts-dev
- 进入项目文件夹ts-dev: cd ts-dev
- 生成package.json文件: npm init -y
- 生成tsconfig.json文件: tsc --init
- 本地安装ts和ts-node:npm i typescript ts-node
3. package.json中配置编译和运行脚本
"scripts": {
"build": "tsc -w", // 编译ts为js,参数-w表示实时监听文件变化
"start": "ts-node ./src/index.ts" //一步到位的运行ts代码
}
4. tsconfig.json常用配置
{
"compilerOptions": {
"target": "es5", // 指定 ECMAScript 目标版本: 'ES5'
"module": "commonjs", // 指定使用模块: commonjs/amd/system/umd/es2015
"moduleResolution": "node", // 选择模块解析策略
"experimentalDecorators": true, // 启用实验性的ES装饰器
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
"sourceMap": true, // 编译后同时生成对应的 map路径映射文件
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"alwaysStrict": true, // 以严格模式检查模块,并在每个文件里加入 'use strict'
"declaration": true, // 生成相应的.d.ts文件
"removeComments": true, // 删除编译后的所有的注释
"noImplicitReturns": true, // 不是函数的所有返回路径都有返回值时报错
"importHelpers": true, // 从 tslib 导入辅助工具函数
"lib": ["es6", "dom"], // 指定编译中的库文件,部分es6新特性需要依赖es6lib
"typeRoots": ["node_modules/@types"],
"outDir": "./dist", //编译后出口
"rootDir": "./src", //根目录入口
"noEmitOnError":true //编译不通过不生成编译后文件,默认会生成
},
"include": [ // **/ 表示递归匹配任意子目录
"./src/**/*.ts"
],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
]
}
5. hello world
- ts-dev下新建src文件夹: mkdir src
- src下新建index.ts: cd src && touch index.ts
- 在index.ts 中写入如下代码
//ts 中,使用冒号: 指定变量的类型,冒号 :的前后有没有空格都可以。
// 注意:函数有参数约束和返回值约束两种约束
function sayHello(person: string):string {
return 'Hello, ' + person;
}
const user = "冷月心"
console.log(sayHello(user));
- 编译 :npm run build
- 项目文件夹下会出现一个dist文件夹
- 编译后的文件dist/index.js
dist/index.js
"use strict";
function sayHello(person) {
return 'Hello, ' + person;
}
var user = "冷月心";
console.log(sayHello(user));
//# sourceMappingURL=index.js.map
6. noImplicitAny属性配置测试
tsconfig.json中我们配置noImplicitAny为true,这意味着在表达式和声明上有隐含的 any类型时报错。这其实是一个严谨的编码习惯,anyScript会削弱静态类型检查的价值。
-
亲测:函数返回值的any检测不到,书写时候要注意
-
修改src/index.ts如下
function sayHello(person) {
return 'Hello, ' + person;
}
const user = "冷月心"
console.log(sayHello(user));
- 编译失败:Parameter ‘person’ implicitly has an ‘any’ type.
7. noEmitOnError属性配置测试
默认情况下,即使编译不通过也会生成编译后文件。设置noEmitOnError属性为true,上述操作6隐含any类型导致的编译不通过,就不会生成编译后文件
基础类型
js基础类型分为两种:原始数据类型和对象类型,其中原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 es6系列中的 symbol,bigint。
1. 布尔/数值/字符串
const bol: boolean = true;
const num: number = 1024;
const userName: string = "冷月心";
//字符串模板依旧能用
const str:string=`${userName}`
2. null和undefined
//这两个类型只有 自己
const onlyNull: null = null;
const onlyUndefined: undefined = undefined;
3. function
声明的函数在调用的时候,参数个数要和声明时候的参数个数保持一致
//没有返回值的函数可以用void声明
const fn1 = (param1:string,param2:number): void => {
console.log("我是没有返回值的箭头函数");
};
function f2(param1:string,param2:number):void{
console.log("我是没有返回值的普通函数");
}
//有返回值的箭头函数声明是这样的
const fn3 = (): string => {
return "冷月心=>"
};
//有返回值的普通函数声明是这样的
function f4():string{
return "冷月心fn"
}
//函数表达式的双向限定
//上述fn1其实只对=右侧做了限制,对左侧并没有
//完善一点,可以这样 => 用来表示函数的定义,左输入类型,需要用括号括起来,右输出类型
const fn1:(param1:string,param2:number)=>void = (param1:string,param2:number): void => {
console.log("我是没有返回值的箭头函数");
};
// 函数的可选参数
// 注意可选参数要在确定参数后
function f5(name:string,age?:number):string{
return "冷月心fn"
}
//函数参数默认值
function f6(name:string,age:number=18):string{
return `${name}--${age}`
}
//此时可选参数不必一定在确定参数后,但是调用有问题
function f7(name:string,desc?:string,age:number=18):string{
return `${name}--${age}--${desc}`
}
//剩余参数
function f8(...args:number[]):number[]{
return args
}
console.log(f8(1,2,3,4,5))//[1,2,3,4,5]
4. void声明变量
void也可以用来声明变量,但只能作用在undefined身上,null也不行。只有tsconfig.json中strictNullChecks属性设置为false时,null才能赋值给void
const u:void=undefined;//这是个鸡肋用法,基本不会这么用
const n:void=null;//这样会报错
const age:void=18;//这样也会报错
5. symbol
symbol使用依赖es6编译辅助库 ,tsconfig.json lib[“es6”]
const sym1:symbol = Symbol();
const sym2:symbol = Symbol();
console.log(sym1===sym2)//false
6. bigint
bigint可以安全地存储和操作大整数, 目前兼容性不是很好
// 超出最大整数的计算会超精度,得不到期望值
const max = Number.MAX_SAFE_INTEGER;
const max1 = max + 1
const max2 = max + 2
console.log(max1===max2)// true ,实际应该不相等
// 需要将数值转成BigInt计算,不会超精度,以下为js代码
const big_max = BigInt(Number.MAX_SAFE_INTEGER);
//这里n是bigint的标志,且bigint和number是完全不同的两个类型
//ts中使用bigint,不支持1n,2n,但是可以BigInt(1),BigInt(2)代替
const big_max1 = big_max + 1n
const big_max2 = big_max + 2n
console.log(big_max1 === big_max2) // false 这才是预期结果
任意值
任意值(any/unknown)用来表示允许赋值为任意类型
- 声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
- 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型(隐式,noImplicitAny属性可以校验到)
- 了解后分分钟忘了即可,这不是什么好习惯
1. any
//非any类型不可以跨类型赋值
let str1:string="冷月心";
str1=123//报错
//any类型可以跨类型赋值,这就像js本身
//注意,这种属于显式声明any类型,tsconfig.json中noImplicitAny属性校验的是隐式
let str2:any="冷月心"
str2=123//不报错
2. unknown
//any类型下,可以在任意值上访问任何属性,调用任何方法,但是unknown不行
let str3:any="冷月心"
str3.say();//不报错
str3.age //不报错
//unknown是更安全的any
let str4:unknown="冷月心"
str4.say();//报错
str4.age //报错
数组
如果你学过java,那你应该更容易理解ts中数组的两种定义方式,选个喜欢的就好
//像java中的集合+泛型,List<String> arr=new ArrayList();
const arr1: Array<string> = ["冷","月","心"]
//类型后+[],java中也是如此:String [] arr = {"冷","月","心"};
const arr2: string[] = ["冷","月","心"]
//数组的项中不允许出现其他的类型
const arr3: string[] = ["冷","月","心",1024]//报错 1024非字符串
元组
ts中的元组和js的数组很像,可以存储不同类型的值
1. 基本使用
元组使用过程中要保证元组类型定义的顺序和填充类型的顺序一致,数量一致,可以按索引访问
const tuple1: [string, number]=["冷月心",24]//这样是ok的
//按索引访问,其实编译后就是js数组 ["冷月心", 24]
console.log(tuple1[0])//冷月心
//这样会报错
const tuple2: [string, number]=[24,"冷月心"]//顺序不一致
const tuple3: [string, number]=["冷月心"]//缺少
const tuple4: [string, number]=["冷月心",24,25]//多余
2. 元组越界
ts允许向元组中使用数组的push方法插入新元素(但不允许访问)
const tuple5: [number, number] = [1, 2];
tuple5.push(3); // 正常运行
console.log(tuple5); // 正常运行 [1,2,3]
console.log(tuple5[2]); //访问新插入的元素会报错
枚举
枚举类型可以由枚举值得到它的名字,这种感觉像对象的键值对。
- 枚举类型也确实属于对象类型
- ts只支持基于数字和字符串的枚举
- 对于基于数字的枚举类型支持键值对的反向映射 key<=>value
- 基于字符串的不可以反向映射?当然不可以,就是纯js对象
//这样不行
enum Flag{
open=true,
close=false
}
1.数字枚举-默认增长
当我们不在乎成员的值的时候,这种自增长的行为是很有用的,但是要注意每个枚举成员的值都是不同的。
enum Language{
java,
node,
php,
python
}
//可以按值访问
console.log(Language[0])//java
//也可以按键访问
console.log(Language["java"])//0
//打印结构如下
console.log(Language);
{
'0': 'java',
'1': 'node',
'2': 'php',
'3': 'python',
java: 0,
node: 1,
php: 2,
python: 3
}
//编译后代码如下 IIFE传参的形式
"use strict";
var Language;
(function (Language) {
Language[Language["java"] = 0] = "java";
Language[Language["node"] = 1] = "node";
Language[Language["php"] = 2] = "php";
Language[Language["python"] = 3] = "python";
})(Language || (Language = {}));
console.log(Language);
//# sourceMappingURL=index.js.map
//可以看到内层赋值生成的是
{
java: 0,
node: 1,
php: 2,
python: 3
}
//外层赋值生成的是
{
'0': 'java',
'1': 'node',
'2': 'php',
'3': 'python',
}
2.数字枚举-自定义增长
当我们需要成员的值按照我们预期设定的的时候,就需要手动设置枚举的增长值。当然,未设置的值会根据上下文增长.
enum Language{
java=5,
node,
php,
python
}
//打印一下
console.log(Language)
{
'5': 'java',
'6': 'node',
'7': 'php',
'8': 'python',
java: 5,
node: 6,
php: 7,
python: 8
}
//当然你也可以全部手动指定每个值,不连续也可以
enum Language{
java=100,
node=101,
php=103,
python=104
}
3. 字符串枚举
其实就是把上述数字枚举的值换成字符串,但结构有些不同,再次证明,枚举属于对象类型
enum Language{
java="J",
node="N",
php="P",
python="PY"
}
//打印一下
console.log(Language)
{
java: 'J',
node: 'N',
php: 'P',
python: 'PY'
}
//看看编译后的js
"use strict";
var Language;
(function (Language) {
Language["java"] = "J";
Language["node"] = "N";
Language["php"] = "P";
Language["python"] = "PY";
})(Language || (Language = {}));
console.log(Language);
//# sourceMappingURL=index.js.map
4. 异构枚举
这不是新类型,属于衍生类型,包含数字和字符串组合的枚举
enum SwitchEnum {
open = 1,
close= "0",
}
//编译后
"use strict";
var SwitchEnum;
(function (SwitchEnum) {
SwitchEnum[SwitchEnum["open"] = 1] = "open";
SwitchEnum["close"] = "0";
})(SwitchEnum || (SwitchEnum = {}));
//# sourceMappingURL=index.js.map
对象
ts在对象的使用上有着一些限制,必须是特定类型的实例,常常配合interface使用。
- interface定义的属性分隔用分号/逗号/空着 都可以
1. 基本使用
//创建一个空对象
const obj:object={};
//也许你想这样给对象增加属性
//但是不行,会报错,因为最初是一个空对象,自然没有这个name属性
obj.name="tom"
//这就需要好用的interface出场了
//如果你用过java,那应该清楚接口是一套待实现的规范
//和元组规范类似,实例属性与声明类型的属性数量和名称严格一致,不可多不可少
interface User{
name:string
}
const u1:User={
name:"冷月心"
}
//报错,多属性
const u2:User={
name:"冷月心",
age:24
}
//报错,少属性
const u3:User={}
//若属性值为函数,也需要在接口中声明
interface Person{
name:string,
say: (something: string) => string
}
const p:Person={
name:"jack",
say: (something: string) => something
}
console.log(p.say("hello"))//hello
2. 可选属性
有些时候我们希望某些属性是可选的,使用?修饰
interface Stu{
name:string,
desc?:string
}
//此时缺少desc属性也是可以的,因为它是可选属性
const s:Stu={
name:"tom"
}
3. 任意属性
有些时候我们允许任意属性的添加,但是其使用和表现显得有些让人费解
interface Stu {
name: string;
sno?: number;
[prop: string]: any;
}
//这样写ok
const s1: Stu = {
name: 'Tom',
sex: '男'
};
//这样写也ok,可选属性没什么影响
const s2: Stu = {
name: 'Jack',
sex: '男',
sno:10010
};
3.1 费解点一
const s: Stu = {
name: 'Join',
sex: '男',
sno:10010,
className:"A班",
desc:"热爱编程"
};
3.2 费解点二
prop: string: any; 也许你觉得任意属性只能是string类型,其实是any
//这些都没问题,正常运行
const s: Stu = {
name: 'Join',
sex: '男',
sno:10010,
className:"A班",
desc:"热爱编程",
hobit:["吃饭","睡觉","打豆豆"],
soulmate:{},
flag:true
};
3.3 费解点三
prop: string: any; 也许你觉得[prop:string]是固定写法,其实prop只是个自定义变量,你可以简写成p,甚至a,b,c,d,但是只有prop: string,[prop: number] 这两种写法
- 你以为[prop: number]意味着属性值可以是number或any??
interface Stu {
name: string;
[prop: number]: any;
}
//以下皆报错
const s1: Stu = {
name: 'Jack',
sex: '男',
};
const s2: Stu = {
name: 'Jack',
age: 18,
};
const s3: Stu = {
name: 'Jack',
hobit:["吃饭","睡觉","打豆豆"],
};
const s4: Stu = {
name: 'Jack',
soulmate:{},
};
const s5: Stu = {
name: 'Jack',
flag:true
};
//好的,关键点来了
//[prop:number]:any,意味着属性名必须是数字,值可以任意,以下都ok
const s6: Stu ={
name:"jack",
0:true,
1:[],
2:{},
3:"冷月心",
4:Symbol()
}
4. 对象类型的子类型
//看看对象类型的子类型
enum Like {
coding
}
const arr:number[]=[1,2,3];
const tuple:[string,number]=["冷月心",1024]
const fn=():void=>{}
//之前我们说过,ts不允许跨类型赋值
//以下代码都会正常运行,这就意味着它们都是对象子类型
obj=Like;
obj=arr;
obj=tuple;
obj=fn;
5. 只读属性
有时我们希望对象某些属性自赋值后不被更改,使用readonly修饰即可
interface Stu {
readonly sno:number
}
//学生学号自赋值后不允许更改
const s:Stu={
sno:10010
}
s.sno=10011 //报错 Cannot assign to 'sno' because it is a read-only property.
参考链接
- 掘金ts小册,https://juejin.im/book/5da08714518825520e6bb810
- ts中文手册,https://zhongsp.gitbooks.io/typescript-handbook
- ts入门教程,https://ts.xcatliu
更多推荐
ts基础教程(一)
发布评论