time: 2018.8.09
update: 2020.12.28
update: 2021-09-16 19:24:39
update: 2022-03-29 14:55:20
update: 2022-08-08 19:32:54
一直都没有怎么去学习 typescript ,但是今天阅读 antd 源码的时候,发现它是用 ts 写的,大部分源码都能看懂,但是它的一些语法不是很懂,比如 export default class Button extends React.Component<ButtonProps, any>
,它尾巴上的 <ButtonProps, any>
是什么,是 React.Component 自带可以这样子写的吗,还是 ts 赋予的?
目录
1 typescript 简介
2 typescript 基本语法
2.1 interface
2.2 array
2.3 function
2.4 class
2.5 declare
2.6 module
3 tsx
4 泛型
5 技巧
6 与es6 class区别
7 ts 踩坑归纳
8 vue3 使用 ts
9 关键字
问题:
export const createApp = ((...args) => {}) as CreateAppFunction<Element>
let renderer: Renderer<Element | ShadowRoot> | HydrationRenderer
后面的 Renderer 是复合声明吗?export * from "@vue/runtime-dom";
在 .d.ts 中 import 导入的是什么?优势
目的:提供类型系统,完善 javascript 过于灵活导致的代码质量参差不齐的问题
.ts
tsc greeter.ts
function greeter(person: string)
,可用类型: string 、interface、any、boolean、number、Array<number>
、void、null、undefinedinterface
关键字,使用不需要 implements ,由 class、object 去实现class
,构造函数 constructor
,如果在构造函数的参数使用 public
关键字,等于创建了同名的类成员变量,可以直接通过类的实例 + .
方式访问as
关键字,可用于接口、类的断言,用以解决 ts 报错的不确定性问题。let strLength: number = (someValue as string).length;
;也可以使用 (<string>someValue).length
来做类型断言类型别名:使用 type 关键字,type NameOrResolver = Name |
NameResolver |
enum
关键字,enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat} 约定取值范围,会被编译为从 0 开始递增的数组。在 ts 中作为一种数据结构,编译之后为数组和对象的结合体|
问题:es 函数默认值是 =
还是 :
?
答:是 = ,: 是类型
类型声明
普通类型:采用冒号 : ,例如 let myFavoriteNumber: string |
number | boolean; |
let hello: Array<number>
,类数组 let hello: IArguments = arguments
interface
声明属性及值类型,或者简单对象则使用 object
声明对象声明方式
let hello: {
first: string
}
hello = {
first: 'world'
}
尖括号里面始终表示类型,可以是基础类型,有可以是定义好的 interface、object 等数据类型,也又可能是定义的泛型
interface
关键字定义,内部数据项要求实例严格保持一致interface Person {}
;
结尾?
[propName: string]: string | number;
;它会限制所有接口属性类型;可以添加任意多个属性readonly id: number;
interface MyArray {
[index: number]: string
}
interface MyFun {
(source: string): boolean
}
作用-类型声明:用于对象类型参数声明,比如定义变量、函数参数、函数本身、类等
const enum
定义常数枚举中不能包含计算值,只能是固定值declare enum
定义外部枚举,编译效果同普通枚举一样,也可以结合常数枚举和外部枚举?
定义,但是函数可选参数之后不能再有其他非可选参数了定义函数类型方式
完整函数类型
let myAdd: (x: number, y: number) => number =
function(x: number, y: number): number { return x + y }
推断函数类型
let myAdd = function(x: number, y: number): number { return x + y } // 会自动推断出左侧变量 myAdd 的类型
let myAdd: (x: number, y: number) => number = function(x, y) { return x + y } // 会自动推断出右侧函数的参数和返回值类型
简化函数类型
let myAdd = function(x: number, y: number) { return x + y } // 会自动推断出左侧变量 myAdd 的类型、右侧函数返回值类型
函数重载
function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {}
class 包含了 属性、constructor、方法
Abstract:Abstract class | method,属于基类,只能被继承,不能被实例化;抽象方法必须被实现 |
声明文件 .d.ts
declare
、interface
关键字开头定义全局声明语句,如果全局定义了变量,那么内部可以直接使用吗?types
字段指向项目的声明文件。如果项目根目录存在 index.d.ts,那么则不需要指明该字段作用: 编辑器定位、代码补全,不会影响到实际代码运行
特点
在 nodejs 中,默认采用的是 commonjs 规范,如果想使用 es6 规范,则必须声明为 .mjs,在 ts 中对于模块有不同的实现
先来看看 ts 默认支持的类型
联合类型:let hello: string | number |
内置对象: let hello: Boolean | Error | Date | RegExp | HTMLElement | NodeList |
let hello: Array<string>
,let hello: <T>(x: T) => T = function yo<T>(x: T): T {}
type 的作用
定义字符串字面量类型:type Names = ‘jack’ | ‘tom’ | ‘andy’; 然后使用 function hello(name: Names): any {} 即可。它的作用是限制了对应参数的取值只能是字面量中的一个 |
type vs interface
编译结果
enum Tristate {
False,
True,
Unknown
}
// compile result
var Tristate;
(function(Tristate) {
Tristate[(Tristate['False'] = 0)] = 'False';
Tristate[(Tristate['True'] = 1)] = 'True';
Tristate[(Tristate['Unknown'] = 2)] = 'Unknown';
})(Tristate || (Tristate = {}));
解析特点
也可以自定义每一项的值
enum EvidenceTypeEnum {
UNKNOWN = '',
PASSPORT_VISA = 'passport_visa',
PASSPORT = 'passport',
SIGHTED_STUDENT_CARD = 'sighted_tertiary_edu_id',
SIGHTED_KEYPASS_CARD = 'sighted_keypass_card',
SIGHTED_PROOF_OF_AGE_CARD = 'sighted_proof_of_age_card'
}
在 typescript 实现的 jsx 命名为 tsx
支持3种模式配置
preserve
: 保留 jsx ,只转换 ts 特有的一些语法,用以后期的转换操作,输出 .jsx
react
: 生成 React.createElement
,直接转换,输出 .js
react-native
: 保留 jsx ,只转换 ts 特有的一些语法,用以后期的转换操作,输出 .js
注意
let foo = <foo>bar
,应该使用 let foo = bar as foo
比如
interface Props {
foo: string;
}
class MyComponent extends React.Component<Props, {}> {
render() {
return <span>{this.props.foo}</span>
}
}
<MyComponent foo="bar" />; // 正确
<MyComponent foo={0} />; // 错误
这里解释了最开始提出的问题,React.Component 的尾巴上的 <Props, {}>
这个是什么。原来这个是它的类型定义,是typescript约定的,不是 react 的
泛型是使用尖括号定义泛型变量,<T>
,其他使用跟函数、数组这些没变化。
在定义函数、接口、类时不指定类型,在使用时再确定。一般很少使用泛型,都使用 any 替代了
注意这几个泛型例子的使用,泛型变量定义的位置 demolet arr: Array<number> = []; function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]]; } swap([7, 'seven']); // 相当于使用了类型推论 swap<number, string>([7, 'seven'])
泛型函数
let myswap: <T, U>(tuple: [T, U]) => [U, T] = function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]] } // 通过表达式声明时,函数泛型前需要命名
泛型接口
interface GenericIdentity<T> { (arg: T): T; } function identity<T>(arg: T): T { return arg; } let myIdentity: GenericIdentity = identity
泛型类
class hello<T> {}
泛型继承接口 T extends,也叫泛型约束
interface hello { world: string; } function identity<T extends hello>(arg: T): T { return arg.world }
function conbine<T extends object, U extends object>(a: T, b: U): T&U {
return Object.assign(a, b)
}
(window as any).hello
这里总结 ts 开发中遇到的问题
生成错误:TypeError: Cannot read property ‘isAbsolute’ of undefined
原因分析:ts 编译时将 es6 import 转为 require 后,使用时默认会去读取 default 作为入口属性,但是标准包在 commonjs require 时,并没有 default 属性
解决方案:修改 ts 编译配置 tsconfig.json "esModuleInterop": true
,这个会默认包裹一层 default 属性
项目使用 vite 作为构建工具
defineComponent
包裹组件,实现类型推断defineProps
, defineEmits
定义 props 和 emits,实现类型推断问题:为什么要使用 defineComponent 包裹组件?
总结:vue3 只是提供了 ts 需要的组件类型,这些方法执行的结果还是 ts 代码,没有对 ts 做啥特殊处理
.ts
的转译,生成 .jsvue-tsc
工具做类型检查使用 volar
主要是写 interface, type, .d.ts 文件
原则是自己编写的组件需要被其他地方引用时,确定好相互之间的参数格式及返回值,提高函数的可读性。
可以编写在组件内部、compositionapi、*.d.ts 文件中
keyof, extends, in, Pick, Record, 同态
名称:索引类型查询操作符
作用:用于限制属性名范围。比如函数参数必须是接口中的一种
索引访问操作符:T[K]
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']);
使用方式解析
keyof 用于获取 interface | 字符串字面量 的索引集合 |
keyof T
可以理解为返回的是 name | age
名称:遍历属性
使用场景:属性映射
interface PersonPartial {
name: string;
age: number;
}
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type ReadonlyPerson = Readonly<PersonPartial>;
使用方式解析
in
+ keyof
关键字[]
包围内部原理: