代码示例传送门
类型检测问题
代码对比
- JavaScript
// foo没有进行参数检测
// 1. 参数类型检测
// 2. 传入参数内容检测
function foo(message) {
console.log(message.length);
}
foo("Hello JavaScript")
// 不传参数,undefined的length不存在
// 只能在执行的时候报错误
foo()
// foo()后续代码无法执行
console.log("Is here!");
- Typescript
let info: string = "hha"
// 不同类型不能赋值
// info = 123
// 默认所有ts文件都是同一个作用域,命名重复会报错
function fn(message: string) {
console.log(message.length);
}
// 未传参数报错
fn() // 代码未执行就报错提示
一目了然JavaScript和Typescript之间的区别,变量的类型检测
变量定义
变量定义的格式
ts变量定义格式 var/let/const 变量名: 类型注解 = 值
值 是一个表达式也没关系,只要能够拥有类型
var name: string = "coder"
let age: number = 18
const height: number = 1.90
// string: TypeScript中字符串类型
// String:JavaScript中的字符串包装类的类型
// 大小写注意区分
const message: string = "Hello TypeScript"
// foo没有添加类型注解
// 赋值时默认会将值的类型给变量定义类型 ————类型推导(inter)
// 所以 foo 的类型是 string
let foo = "foo"
注意: ts文件共用一个作用域,因此命名可能重复; 可通过模块化的方式,拥有自己的作用域
解决方案:在文件最后一行加上该代码
export {}
number
let num: number = 123
// num = "123"
let num1: number = 100
let num2: number = 0b111 // 二进制
let num3: number = 0o456 // 八进制
let num4: number = 0x123F // 十六进制
console.log(num1, num2, num3, num4);
boolean
let flag: boolean = true
flag = false
flag = 20 > 30
string
let message: string = "hello"
// 默认情况下,能够推导出类型可不写
let word = "typescript"
// 参数中无法推导,则需要确定参数类型
function foo(message: string, word: string) {
let newMes = `${message} ${word}`
console.log(newMes);
}
array
// 确定:names是一个数组类型
// 同一个数组类型中的数据最好是固定类型的
// 数组中存放不同类型数据是不好的习惯
const names: Array<string> = [] // 不推荐(jsx中存在冲突)
const namesArr: string[] = [] // 推荐
// 会报错,因为限定了数组中的类型
// names.push(123)
names.push("tom")
namesArr.push("jack")
object
// 存在弊端:无法取出info的属性
// const info: object = {
// name: "tom",
// age: 18
// }
// 推荐写法
const info = {
name: "jack",
age: 18
}
null 和 undefined
// null类型只有一个值————null
const n1: null = null
// 默认是 any类型
let n2 = null
n2 = "string"
// undefined只有一个值——undefined
let u1: undefined = undefined
Symbol
const title1 = Symbol("title")
const title2 = Symbol("title")
const info = {
title: "hello symbol",
[title1]: "hello",
[title2]: "typescript"
}
TypeScript数据类型
下面介绍的是typescript自带的数据类型
any类型
讨巧类型,不想限定JavaScript变量添加具体的数据类型(js原生一样)
// 进行一些类型断言 as any
let message: any = "hello world"
// 类型注入为any,message可以是任何类型
message = 123
message = "hello typescript"
message = () => {}
unknown类型
当类型不确定的时候,避免变量被滥用,可以使用unknown类型,不推荐使用any类型。
与any类型的区别:
- unknown类型只能赋值给 any 和 unknown 类型
- any类型可以赋值给任意类型
function foo() {
return "abc"
}
function bar() {
return 123
}
let flag = true
let result: unknown
if (flag) {
result = foo()
} else {
result = bar()
}
// result是unknown类型时,赋值给其他类型会报错
// let info: string = result
console.log(result);
void类型
函数返回值的默认类型
// 当函数没有返回值的时候,默认会加上void类型。可不写
function sum(num1:number, num2: number): void {
console.log(num1 + num2);
}
sum(20, 30)
never类型
函数注入类型 never:表示永远不会有返回值
function foo(): never {
// 死循环
while (true) {
}
}
应用场景
// | :是联合类型,代表 既。。 也可。。。
// 当message只是string和number类型时的函数,突然想要添加验证boolean类型的验证时
// 在参数中添加 boolean并不够,default中会报错,never表示永远不会
// 因此需要添加一个case 验证boolean类型才能通过
// 这样函数减少了出错的机会
function handleMessage(message: string | number | boolean) {
switch (typeof message) {
case 'string': console.log("string"); break;
case "number": console.log("string"); break;
case "boolean": console.log("boolean");break;
default:
const check: never = message
}
}
tuple元组
tuple元组:多种元素的组合
// any
const info: any[] = ["tom", 18 , true]
// 拿到any数组的元素,类型推导默认为any类型
// newName类型是 any
const newName = info[0]
console.log(newName.length)
// 元组类型,指定数组中的各元素的类型
const infoArr:[string, number, boolean] = ["why", 18 , true]
// myName类型是 string
const myName = infoArr[0]
console.log(myName);
应用场景
// 泛型 T: 类型推导
function useState<T>(state: T) {
let currentState = state
const changeState = (newState: T) => {
currentState = newState
}
// any 数组
// const arr: any[] = [currentState, changeState]
// return arr
const tuple: [T, (newState: T) => void] = [currentState, changeState]
return tuple
}
// 使用 any数组的话,每一项都是any,情况不太友好
// 使用元组的话,每项的类型都能够被限定
// (将any替换成 T)使用泛型,能够间接的进行类型推导,更能明确变量的类型
const [counter, setCounter] = useState(10)
setCounter(1000)
TypeScript类型补充
1.函数参数和返回值
// 给参数加上类型注解
// 给返回值加上类型注解(通常情况下可不写)
function total(num1: number, num2: number) :number{
return num1 + num2
}
写了几个参数,必须传几个参数;不然会报错
2.匿名函数参数类型
const names = ["abv", "tom", "jack"]
// 匿名函数可以不写参数类型
// item 类型根据上下文的环境推导出来的,因此可不写类型注解
names.forEach((item) => {
console.log(item);
})
3.对象类型
// point : x, y
function printPoint(point: {x: number, y: number}) {
console.log(point.x);
console.log(point.y);
}
printPoint({x: 123, y: 456})
4.联合类型
| 联合符号: 表示并集,有两个或以上的类型组成
function printID(id: number | string) {
// 使用联合类型的时候,需要注意不要直接对 参数进行指定类型的方法操作
// 最好先对参数进行类型判断,再分段操作
if(typeof id == "string") {
console.log(id.toUpperCase());
} else {
console.log(id ++);
}
}
// 可传 number 可以传 string
printID(1)
printID("abc")
5.可选类型
标识符 ? : 表示该变量是可选的(可传可不传)
// point : x, y
// point 拥有第三个参数 z
// 使用 ?: 表示该变量是可选的(可传可不传)
function printPoint(point: { x: number, y: number, z?: number }) {
console.log(point.x);
console.log(point.y);
}
printPoint({ x: 123, y: 456 })
printPoint({ x: 1, y: 2, z: 3 })
export { }
可选类型和联合类型的关系
当参数是可选类型的时候;他其实本质上是一个: 类型 | undefined 的联合类型
function foo(message?: string) {
console.log(message);
}
// 不传参,默认是传入undefined
foo()
6.类型别名
type 关键字:用于定义类型别名
type UnionType = number | string
type PointType = {
x: number,
y: number,
z?: number
}
// function printPoint(point: { x: number, y: number, z?: number }) {
// console.log(point.x);
// console.log(point.y);
// }
function printPoint(point: PointType) {
console.log(point.x);
console.log(point.y);
console.log(point.z);
}
printPoint({ x: 123, y: 456 })
printPoint({ x: 1, y: 2, z: 3 })
function printID(id: UnionType) {
if(typeof id == "string") {
console.log(id.toUpperCase());
} else {
console.log(++ id);
}
}
printID(1)
printID("abc")
7.类型断言
类型断言: 将大范围的类具体化到指定类型
// 存在类型推导,可不写类型注解
const el = document.getElementById("app") as HTMLImageElement
el.src = "url"
// 类继承:Person 是 student的父类
class Person { }
class Student extends Person {
study() { }
}
// 宽泛的类型转化成具体的类型
function sayHello(p: Person) {
(p as Student).study()
}
const stu = new Student()
sayHello(stu)
// 慎用 !!!!!
const message = "hello typescript"
const ann: number = message as any as number
8.非空类型断言
标识符 !. : 在变量后面放置,告诉typescript该变量不可能为空
// 不严谨,存在undefined,编译不通过
function foo(message?: string) {
// 1. 逻辑代码块
// if (message) {
// console.log(message.length);
// }
// 2. 告诉typescript,message不可能为空
// 放在 可能为空的变量的后面
// 感叹号: 非空类型断言
console.log(message!.length);
}
9.可选链
es11 的语法 ?. 可选链 表达式:当符号前者不为undefined的时候才去获取后面的值,如果符号前的对象为undefined,则返回undefined
type Person = {
name: string,
friend?:{
name: string,
age?: number
}
}
const info:Person = {
name: "tom",
friend: {
name: "jack"
}
}
console.log(info.name);
// 如果符号前的对象为undefined,则返回undefined
console.log(info?.friend?.name);
console.log(info.friend?.age);
10. !! 运算符
运用 ! 取反运算符,双重使用将其他类型的变成boolean类型
const message = "hello typescript"
// const flag = Boolean(message)
// 可以使用该方法简化操作,利用js!取反操作符
// 并不是ts新增的
const flag = !!message
console.log(flag);
11. ?? 运算符
es11 的新特性并不是ts新增的 ; || 短路或(左边为false,运算右边)类似,这更多用于if判断里面
逻辑操作符,当左边是 null 或者 undefined 则返回右侧的值
let message: string |null = null
const content = message ?? "hello typescript"
// 同三元运算符
// message ? message : "hello typescript"
console.log(content);
12.字面量类型
// "Hello world" 也可以作为类型的,叫做字面量类型
const message: "Hello world" = "Hello world"
// 字面量类型的意义,就是必须结合联合类型
type Alignment = 'left' | 'right' | 'center'
let align: Alignment = 'left'
align = 'right'
align = 'center'
// 报错
// align = "hhha"
13. 字面量推理
type Method = 'GET' | "POST"
function request(url: string, method: Method) {}
type Request = {
url: string,
method: Method
}
// 防止对象出错,可以在别名中嵌套别名(推荐)
const options: Request = {
url: "https://www.gaoshi.org/jqk",
method: "POST"
}
// 同样也可以使用断言
request(options.url, options.method as Method)