类型判断
- 不设置类型会自动进行类型推论
// basic types
let isDone: boolean = false
let age: number = 10
let firstName: string = 'viking'
let message: string = `Hello, ${firstName}`
let u: undefined = undefined
let n: null = null
let num: string = null
let notSure: any = 4
notSure = 'maybe a string'
notSure = true
notSure.myName
notSure.getName()
// array
let arrOfNumbers: number[] = [1, 2, 3, 4]
arrOfNumbers.push(3)
// tuple
let user: [string, number] = ['viking', 20]
user = ['viking', 30]
// function
function add(x: number, y: number, z:number): number {
return x + y
}
//let result = add(2, 3)
let add2 = (x: number, y:number): number => {
return x + y
}
// 冒号后面等号前面都是声明类型
const add3:(x: number, y:number, z?: number) => number = add
const add4 = add2
我们发现当const add4 = add2也是可以的, add3会自动获得类型, 这种特性在 ts 中称为 类型推论(type inference)
ts会在初次没有指定类型的时候自动添加一个类型 又叫类型推论(type inference)
let str = 'str' // 类型推理为string
str = 122 // 这段代码爆红,因为类型推断为string 不能赋值 number
str = 122这段代码爆红,因为类型推断为string 不能赋值 number
对象类型判断 (interface)
interface是数据的共性的抽象 有自己的属性 只是在开发过程中做语法提示校验的工具 编译后不存在
interface接口的功能
- 对对象的形状(shape)进行描述 (抽象的契约)
interface Person {
readonly id: number; // readonly 只读属性 (不能修改)
name: string;
age?: number; // ? 参数可有可无
}
let viking: Person = {
name: 'viking',
age: 20,
id: 1
}
const sum = (x: number, y: number) => {
return x + y
}
interface ISum {
(x: number, y: number): number
}
// 定义函数类型
const sum2: ISum = sum
interface RandomMap {
[propName: string]: string; // 使用[] 去定义参数名的可变性
}
const test: RandomMap = {
a: 'hello',
b: 'test',
c: 'test'
}
interface LikeArray {
[index: number]: string
}
const likeArray: LikeArray = ['1', '2', '3']
// duck typing
interface FunctionWithProps {
(x: number): number;
name: string;
}
const a: FunctionWithProps = (x: number) => {
return x
}
a.name = 'abc'
interface 这种模式(只要你有这个功能就适配) 又叫 duck typing
泛型
- 诞生原因:
- 类型推断 并不能推论出函数里参数的流向
- 函数体内可能会做一些副作用的操作,想要 执行操作的时候在返回类型
- 定义:范性是在定义函数、接口和类的时候不预先制定类型,而在使用的时候制定类型的一种特性(调用时定义类型)
范型定义在函数上
- 类型推断 并不能推论出函数里参数的流向
如下代码:定义一个函数,想通过传递参数不同返回对应的类型,类型为string, 但是 类型推断 并没有检测到, 导致 result 设置为number 并不会爆红
function echo(arg) {
return arg
}
const result:number = echo('str') // echo('str')的返回类型是arg
result.toFixed() // 这快ts语法检测不会检测到报错
范性给函数建立了一个通道,打通函数参数的类型,可以到函数内部,和它的输出,当遇到范型的时候,把调用时传递参数的类型自动流入函数体内
如下代码,将泛型的名称定义为<T>(T只是一个站位,在使用的时候指定)
function echo<T>(arg: T): T {
return arg
}
const result = echo('str')// 指定为string
const test = echo(123)// 指定为number
范性传入多个值:
function swap<T, U> (tuple: [T,U]): [U, T] {
return [tuple[1], tuple[0]]
}
const result = swap(['string', 123])
对象类型别名(type)
有些类型太长,所以需要定义个别名
let sum:(x: number, y: number) => number
type PlusType = (x: number, y: number) => number
let sum2: PlusType
交叉类型: 类型合并
interface IName {
name: string
}
type IPerson = IName & { age: number } // & 合并
let person: IPerson = {name: 'hello', age: 12}
联合类型:xx类型 或者 xx类型
- 当TS不确定一个联合类型的变量到底是什么的时候,只能访问 联合类型中共有的属性
let numberOrString: number | string
numberorString.length // 这快因为 string中有length 而 number 中没有所以报红
有时候需要在不确定类型的时候就访问其中类型的属性和方法,就需要类型断言
类型断言, 告诉ts比它更了解这个变量的类型,不让它爆红
function getLength(input: number | string) {
const str = input as string
if (str.length) {
return str.length
} else {
const number = input as number
return number.toString().length
}
}
interface IName {
name: string
}
type IPerson = IName & { age: number }
interface Person {
name: string
age: number
}
type PersonOptional = Partial<IPerson>// Partial 内置类型在后面高级特性讲
let viking2: PersonOptional = {}
范型定义在副作用上
- 函数体内可能会做一些副作用的操作,想要 执行操作的时候在返回类型
如下代码,在函数里调用请求接口,.then的时候 resp为ary类型(不是想要的)
function withAPI(url: string) {
return fetch(url).then(resp => resp.join())
}
withAPI('github.user').then(resp => { // 这块的resp 为 ary类型
})
需要如下改动,将 GithubResp 类型在 调用时传递通过 <T>(范型)流动
interface GithubResp {
name: string;
count: number
}
function withAPI<T>(url: string): Promist<T> {
return fetch(url).then(resp => resp.join())
}
withAPI<GithubResp>('github.user').then(resp => { // 这块的resp 为 GithubResp
})
范型定义在接口上
代码如下
// import { FunctionComponent } from 'react'
// 原react类型定义
interface FunctionComponent<P = {}>{ // <P = {}> 是默认值的意思
(props: PropsWithChildren<P>, content?:any): ReactElement<ary, ....>; // <P> 在调用的时候定义的类型
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
interface TestProps {
title: string;
desc: string;
}
const Test: FunctionComponent<TestProps> = (props) => {
return (
<>
<h1>{props.title}</h1>
<p>{props.desc}</p>
</>
)
}
Test.propTypes
高级特性
Partial源代码解析
- keyof 字符串字面量(取键值)【知识点】 将一个interface全部变成可选
interface CountryResp {
name: string;
area: number;
population: number;
}
// keyof
type Keys = keyof CountryResp
// lookup types
type nameType = CountryResp['name']
// mapped types
type Test = {
[key in Keys]: all
}
//
type CountryOpt = {
[p in Keys]?: CountryResp[p]
}
extends
- extends场景:
- 范型约束
- extends并不是类上的继承概念,
在定义时并不知道入参的类型,
arg.length不知道是否存在,所以爆红
function echoWithArr<T>(arg: T): T {
console.log(arg.length) // 这块爆红,并不知道范型
return arg
}
const arrs = echoWithArr([1,2,3])
所以,通过extends传来的参数取约束范型T 当前面表达式是面表达式的子级
interface IWithLength {
length: number
}
function echoWithArr<T extends IWithLength>(arg: T): T {
console.log(arg.length) // 这块爆红,并不知道范型
return arg
}
const arrs = echoWithArr([1,2,3])
const str = echoWithArr('123')
const obj = echoWithArr({length: 123})
在TS2.8中负于了extends新用处,就是条件类型关键字
如果 T的子级有null或者undefined 则是 never 否则是 T
type NonType<T> = T extends null | undefined ? never : T
let demo1: NonType<number>
let demo1: NonType<null>
定义文件
假如是在项目被来是用ts写的,则不需要这个文件,库在tsc编译以后会生成 .d.ts 文件
单独写一般是应为原来是js模块,现在想让这个js模块在ts项目中使用。类似注释文件让ts读懂
ts默认加载node_model下的类型文件,所以需要把text.d.ts移入@types
// fetch.js
function myFetch(url, method, data) {
return fetch(url, {
body: data ? JSON.stringify(data) : '',
method
}).then((resp) => resp.json())
}
myFetch.get = (url) => {
return myFetch(url, 'GET')
}
myFetch.post = (url, data) => {
return myFetch(url, 'POST', data)
}
export default myFetch
// 设置为字面量类型 根 keyof一样
type HTTPMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE'
// text.d.ts
declare function myFetch<T = any>(url: string, method: HTTPMethod, data?: any): Promise<T> {}
declare namespace myFetch{
const get: <T = any>(url: string) => Promise<T>
const post: <T = any>(url: string) => Promise<T>
}
export = myFetch //导出
调用
import myFetch from 'myFetch'
myFetch<String>('test', 'POST', {name: 'hello'}).then(data => { // data是string类型了
})
myFetch.get<number>('test').then(data => {
})