TypeScript是⼀个开源的编程语⾔,通过在JavaScript的基础上添加静态类型定义构建⽽成。通过TypeScript编译器或Babel转译为JavaScript代码,可运⾏在任何浏览器,任何操作系统。
TypeScript是JavaScript的⼀个超集,他们之间并不是所属关系,TypeScript扩展了JavaScript弱类型语⾔的限制,增加了更多的模块解析⽅式和语法糖。TypeScript并不是⼀个能独⽴运⾏的语⾔,⼤多数时候他都被转译成 JavaScript运⾏,所以可以简单的认为TypeScript相当于功能更丰富的编译型的JavaScript。
JavaScript本身已完全可以满⾜完整的应⽤开发需求,但在⼤型项⽬协作开发或插件开发的场景中 JavaScript弱类型语⾔的不⾜便暴漏出来。由于JavaScript并⾮编译型语⾔,在代码编写过程中⽆法轻松的实现良好的类型约束和类型推断。TypeScript强类型的约束性以及其⾯向接⼝编程的约束性可以让TypeScript语法开发的应⽤有极强的 维护性,代价是更⼤量的代码篇幅。
TypeScript通过编译后最终还是会转换为JavaScript,最终执行的还是JavaScript,并不能代表性能优势。TypeScript语⾔之所以流⾏,是因为其类型化的JavaScript,在上下⽂阅读时可以提供更好的类型追溯,通过编辑器插件可已实现更有好的提示。
中文文档地址:https://www.tslang.cn/index.html
打开电脑的命令⾏⼯具执⾏TypeScript引擎安装命令:
npm install typescript -g // 全局安装
tsc -v // 查看版本号
新建index.ts文件,用编辑器打开输入以下代码:
let str:string = 'Hello Word';
console.log(str);
打开终端输入tsc
,就自动编译出对应的index.js
文件。输入node index.js
完成终端编译js代码,打印输出Hello Word
;
在JavaScript的基础上新增了几种数据类型,在定义值时要声明值的类型。let 变量名:变量类型 = 变量值
。注意:类型是内置的时首字母建议小写,自定义定义的接口类型建议首字母大写。
let big: bigint = BigInt(10);
let sym: symbol = Symbol("lin");
let num1:number = 1;
let str1:string = "1";
let unde1:undefined = undefined;
let null1:null = null;
let boo:boolean = true;
boo = 'false'; // 报错 不能将类型“string”分配给类型“Boolean”
不清楚用什么类型,可以使用 any 类型。这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。注:切勿滥用,不然就变成anyScript
let num:any = 0;
num = '0';
num = true;
// 上面定义了一个num类型为any,可以给他赋值为任意类型。完全失去了TS的作用
当不知道一个类型具体是什么时,该怎么办?可以使用 unknown
类型,它代表任何类型,它的定义和 any
定义很像,但是它是一个安全类型,使用 unknown
做任何事情都是不合法的。例如:
function getCount(num: unknown) {
return num / 2;
}
// 会提示错误:对象的类型为 "unknown"
// 解决方案
function getCount(num: unknown) {
return num as number / 2; // as为类型断言,将参数转换为number类型
}
void
类型与 any
类型相反,它表示没有任何类型。比如函数没有明确返回值,默认返回 Void 类型
function seeHello(): void {
console.log('hello')
}
never
类型表示的是那些永不存在的值的类型。
有些情况下值会永不存在,比如:
// 异常
function showError(msg: string): never {
throw new Error(msg)
}
// 死循环
function showLoop(): never {
while (true) {}
}
可以直接定义值为对象类型,也可以定义特定的接口对象来规范对象中每个值的类型(后面会提到);
const obj:object = {
name: '小明'
}
obj.age = 23; // 报错 类型“object”上不存在属性“age”
// 解决方案
// Record是TS中内置工具类型,接收两个泛型参数
const obj1:Record<string, any> = {} // 规范Obj1对象每一项键名必须是字符串,值的类型可以是any类型
注意:声明对象时要规范数组每一项的类型,新添加一个不存在的值会导致语法错误。
格式为:let 变量名:数组中类型[] = 变量值
,注意:数组中的值一定要与定义的类型对应上要不然就会报错;
// 定义了一个数组每一项都是number类型的数组
let list: number[] = [1, 2, 3]
list.push(4)
list.push('5') // 报错 类型“string”的参数不能赋给类型“number”的参数。
如果数组想每一项放入不同数据怎么办?用元组类型。
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let list: [number,string,boolean] = [1, '2', true];
list.push(null); // 报错 因为元祖类型中没有null类型定义
TS 定义函数类型需要定义输入参数类型和输出类型。输出类型也可以忽略,因为 TS 能够根据返回语句自动推断出返回值类型。
function add(x:number, y:number):number {
return x + y
}
console.log(add(1,2)); // 3
// 函数表达式
let add2 = (x: number, y: number) => {
return x + y
}
console.log(add(1,4)); // 5
console.log(add(1,'5')); // 报错 类型“string”的参数不能赋给类型“number”的参数。
参数后加个问号,代表这个参数是可选的
function add(x:number, y:number, z?:number):number {
return x + y
}
console.log(add(1,2,3),add(1,2)); // 3 3
跟 JS 的写法一样,在入参里定义初始值。和可选参数不同的是,默认参数可以不放在函数入参的最后面,
function add(x:number = 100, y:number):number {
return x + y
}
console.log(add(1,4)); // 5
console.log(add(undefined,100)); // 200 当x为undefined会使用默认值
函数重载是指两个函数名称相同,但是参数个数或参数类型不同,他的好处显而易见,不需要把相似功能的函数拆分成多个函数名称不同的函数。
function add(x: number[]): number
function add(x: string[]): string
function add(x: number[], y: number[]): number
function add(x: string[], y: string[]): string
function add(x: any[], y?: any[]): any {
if (Array.isArray(y) && typeof y[0] === 'number') {
return x.reduce((acc, cur) => acc + cur) + y.reduce((acc, cur) => acc + cur)
}
if (Array.isArray(y) && typeof y[0] === 'string') {
return x.join() + ',' + y.join()
}
if (typeof x[0] === 'string') {
return x.join()
}
if (typeof x[0] === 'number') {
return x.reduce((acc, cur) => acc + cur)
}
}
console.log(add([1,2,3])) // 6
console.log(add(['1', '2'])) // '1,2'
console.log(add([1,2,3], [1,2,3])) // 12
console.log(add(['1', '2'], ['3', '4'])) // '1,2,3,4'
注意:在 TS 中,实现函数重载,需要多次声明这个函数,前几次是函数定义,列出所有的情况,最后一次是函数实现,需要比较宽泛的类型
interface
(接口) 是 TS 设计出来用于定义对象类型的,可以对对象的形状进行描述。定义 interface 一般首字母大写,如下:
interface Person {
name: string
age: number
}
const son: Person = {
name: '张三'
}
// 报错 类型 "{ name: string; }" 中缺少属性 "age",但类型 "Person" 中需要该属性。
// 变量用了Person接口但是未定义age属性,导致报错;同时多写属性也会报错
注意:interface 不是 JS 中的关键字,所以 TS 编译成 JS 之后,这些 interface 是不会被转换过去的,都会被删除掉,interface 只是在 TS 中用来做静态检查
跟函数的可选参数是类似的,在属性上加个 ?
,这个属性就是可选的。
interface Person {
name: string
age?: number
}
const son: Person = {
name: '张三'
}
如果希望某个属性不被改变,
interface Person {
readonly id: number
name: string
age: number
}
const son: Person = {
id: 2,
name: '张三',
age: 20
}
son.id = 3; // 报错 无法分配到 "id" ,因为它是只读属性
interface 也可以用来描述函数类型
interface ShowFn {
(x:number,y:number):number
}
const add:showFn = (num1, num2) => {
return num1 + num2
}
当一个对象上有多个不确定的属性时,就可以使用自定义属性。
interface ShowRandom {
[key: number]: number
}
const arr: ShowRandom = {
0: 0,
1: 1,
2: 2,
}
// 还可以这样写
const arr1:ShowRandom = [0,1,2];
// 看似想一个数组,其实是一个类数组,不能调用数组的属性和方法
interface 的写法非常灵活,用 interface 可以创造一系列自定义的类型。所以interface 还有一个响亮的名称:duck typing
(鸭子类型)。 例如:
interface ShowFn {
(num1:number ,num2:number):number;
name: string;
}
const fn:ShowFn = (num1,num2) => {
return num1 + num2
}
fn.name = "函数";
上面这个接口中函数类型添加了一大堆属性,完全四不像,但是却是完全正常的工作。这就是 duck typing 和 interface,非常的灵活。