let a: string = "abc" // 规定值的类型
function fn(arg: number): number { //这里规定了参数 arg 的类型以及函数返回的类型
return arg + 1
}
目录
- ts基本类型
- js 的七种基本类型
- Enum
- Any
- Void
- Tuple
- Null & Undefined
- 数组类型
- 伪数组类型
- 函数类型
- this
- 重载
- 描述一个函数
- 接口 interface
- 基本用法
- 只读属性
- 可选属性
- 方法
- 任意属性
- 获取接口的属性类型
- 基本用法
- 继承 extends
- 类 class
- 类的继承
- 修饰符
- public
- private
- protected
- get / set
- static
- 抽象类
- 语法 & 关键字
- Partial
- type
- keyof
- in
- [ ]
- &
- |
- Readonly
- Partial
- declare
- 类型断言(类型转换)
- < >
- as
- 其它
- 类型兼容
- soundness
- 用 ts 添加 window 变量
- 给 React 添加全局属性
内容
ts 的基本类型
js 的七种基本类型:
string, number, null, undefined, object, symbol, boolean
其它:
enum, any, void
enum 枚举
//申明一个枚举 Gender
enum Gender {
Man = 'man', //这里给 Man 设置了值,默认情况下(不写 = 'man')是 0,
Woman = 'woman',
}
// 使用:
let a: Gender = Gender.Man //使用的时候,就直接用 枚举名称.key 的格式,就不会写错。
any
表示任何类型
Void 空类型
function fn(arg: number): void {
//不写 return
}
Tuple
规定了一个 数组 的 item 类型
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error
Null & Undefined
undefined(null) 可以理解为 空,默认情况下null和undefined是所有类型的子类型。就是说你可以把 null和undefined赋值给任何类型的变量。
在 ts 中,null 与 undefined 没有区别
let a: string = undefined // 可以
数组
list1: Array //数组
list2: Array //数字组成的数组
list3: number[] // 等价于上面
list4: Array> // 数字组成的数组所组成的数组 ……
list5: number[][] //等价于上面
ArrayLike
伪数组的定义(源码):
interface ArrayLike {
readonly length: number;
readonly [n: number]: T;
}
函数
function fn(arg1: string, arg2 = 2): void{
//arg2 可以不写类型,ts 会自动知道是 number
//...
}
this
我们可以规定 this 的类型:
interface Human {
name: string;
age: number;
}
function fn(this: Human){
console.log(this)
}
fn.call({name: 'abc', age: 18}) // 前面规定了 this 必须是符合 Human 的对象,所以这里必须用 call,
fn() // 会报错
注意,ts 语法中,函数的参数数量必须与声明时一致。
重载
指的是一个函数有不同的调用形式
//重载,要么满足第一个,要么满足第二个
function add(n1: number, n2: number);
function add(n1: string, n2: string);
function add(n1, n2) { // 最后这个函数只是实现,不代表“接受任何参数”
return n1 + n2
}
重载优于泛型的地方:
重载可以规定某几种,但是泛型似乎做不到 …… 泛型太泛了 ……
描述一个函数:
(写法与普通对象不一样)
interface Fn {
(a: string, b: string): string; //注意,这里的 a / b 没有什么意义,我们在后面创建一个函数的时候,不一定要用这两个名字
hi(): void // 这个是这个函数的方法
}
//但是,这样的规范,使得我们想要给一个函数加上属性方法,变得不容易,比如,上面我们给函数描述了一个 hi 方法,但是我们如何用呢?
let fn: Fn = (
(): Fn => { // 这个箭头函数的意思是,你会返回一个符合 Fn 描述的函数,所以 fn 得到的就是这个箭头函数返回的(符合描述的)函数
let x: any = function(a: string, b: string): string {
return a + b
}
x.hi = function(): void {
return 1
}
return x
}
)()
interface
接口(interface)就是用代码描述一个对象必须有什么属性(包括方法),不能少,也不能多
interface XXX {
abc: number;
readonly name: string; // 只读 readonly
likedGame?: Array; // 可选
add(a:number, b:number): number; // 方法
(a:number, b: number): number; // 可不写函数名
}
任意属性
如果我们想要任意属性,用:
[propName: string]
(定义了任意属性取 string 类型的值)
注意:一旦定义了任意属性,那么确定属性(:)和可选属性(?:)的类型都必须是它的子集(即符合任意属性规则)。
interface YYY {
width: number; //因为 propName 的存在,这里只能是 number,如果是其它且不是 number 的子集,会报错
height: number;
[propName: string]: number;
}
let a: YYY = {
width: 1,
height: 2,
age: 23, // 可以额外的属性
color: "red", // 不行,会报错
}
获取接口中的某个类型
用方括号
interface YYY {
abc: number
}
let b: YYY["abc"] = 1 // 可以这么写,b 的类型是 number, 不能写成 YYY.abc 的形式
继承
使用 extends 关键字,interface 还可以同时继承多个!
用 extends
注意,interface 还可以继承多个!
interface Animal {
move(): voil;
}
interface Organic {
c: boolean;
o: boolean;
h: boolean;
}
interface Human extends Animal, Organic {
name: string;
age: number
}
//必须满足 Human / Organic / Animal 三个接口
let man: Human = {
age: 18,
name: 'kala',
move(){
console.log(1,2,3,4)
},
c: true,
o: true,
h: true
}
也可以采用另外一种方式继承,一个接一个继承。
注意,如果两个接口拥有同样的属性描述,那么没办法一同被继承,也不能一个继承另一个,除非两个类型描述是一样的。
类 class
类的定义与接口类似,就是用来告诉你一个对象必须有什么属性
接口是低配版的类
类是高配版的接口
类的基本特点:
1.有 constructor,让class 变得非常灵活
2.类的属性:static
3.对象的属性:除了 static
4.继承要用 super
class Human {
static xxx = 1 // static (静态属性)的意思是,这个是 Human 的属性,不是实例的属性, 我们没办法直接通过 Human.xxx 来定义
name: string
age: number
constructor(name = "kala", age = 15) { //设置默认值
this.name = name
this.age = age
}
move(): void {
}
}
//使用:
//可以这么用:
let a = new Human("a", 1)
//还可以当做接口使用
let b: Human = {
name: "a",
age: 1,
move(){} // 注意,这里要手动写 …… 不过后面也会提到,作为接口、与作为类,对于方法的处理上有着本质的区别
}
类的继承
注意要在 constructor 中使用 super, 相当于调用了父类的 constructor,是否传参取决于父类 constructor 是否需要参数。
class A {
constructor() {
console.log('ok')
}
}
class B extends A {
constructor(){
super()
}
}
修饰符
1.public : 公开属性
2.private : 私有属性,升级版的局部作用域;只能在当前 class 中用,不能在子类中使用;
3.protected : 保护,既不是完全公开,也不是完全不公开,即‘有限公开’; 可以再当前 class 或者子类 class 中使用
4.get / set
5.static (静态属性)的意思是,这个是 Human 的属性,不是实例的属性, 我们没办法直接通过 Human.xxx 来定义
private
以前,我们通过加一个下划线来让别人知道,这个东西不是给别人用的'_secret', 在 typescript 中很简单,只要在属性名前面加上 private
,不过,我们依然会写成 _secret
的形式;
//私有属性
class Human {
static xxx = 1
name: string
private _secret: string // 私有属性,别人用不了
private _age: number
get age(){
return this._age
}
set age(value: number){
if(value < 0) {
this._age = 0
} else {
this._age = value
}
}
constructor(name = "kala", age = 15) {
this.name = name
this.age = age
}
}
//以上的设计,我们没法直接访问 `_age`,因为是 private 的,但是我们又可以通过 age 去设置它,因为 age 的环境是class 内部 ……
static 与 private 的区别
static 静态属性,被static修饰的变量和方法类似于全局变量和全局方法,可以在不创建对象时调用,当然也可以在创建对象之后调用。
private 私有属性,对于用户来说,是不可见且不可访问的
抽象类 (abstract)
可以叫 “爸爸类”:专门当别的类的爸爸的类
也可以叫做 “没有写完的类”:只描述有什么方法,并没有完全实现这些方法
由于这个类没有写完,所以不能创建出对象。
interface 不能写函数的实现,直接是 xxx(): void
class 可以要函数的实现 :xxx(): void { ... }
abstract 就是把 class 中的函数‘降级’到原来的写法 abstract xxx(): void
也就是说:
interface: 知道有这么个方法
class: 知道一个具体的方法
abstract: 知道有这个方法,并且后续的之类需要把这个方法具体化
举个例子,Animal 有个 calls (叫声)的函数,但是我们不确定每个动物具体怎么叫,于是我们就可以在 Animal 中写 abstract calls(): void
也就是,如果类中的一个方法不好写实现,就在前面加个 abstract,
abstract 小结:
1.如果一个类的某个函数是抽象函数,那么我们要在这个类的前面也加上一个 abstract,比如 abstract Animal
;
2.这个类不能用来创建对象;
3.子类继承这个抽象类之后,要把这儿抽象函数完善。
类型断言(类型转换)
为了防止 ts 报错,主动告诉编译器,我知道这个东西的类型是什么
1.使用尖括号
2.使用 as 关键字
(a).split('') // 意思是说,我知道 a 类型是 string
(a as string).split('') // 等同于上面那一句
注意,当你在TypeScript里使用JSX时,只有 as语法断言是被允许的。
语法 & 关键字
Partial
Partial // 得到的属性与 XXX 里面的属性一样,不过都是可选 :?
type
alias 别名
type Layout = React.FuncionComponent & { Header: React.FunctionComponent }
keyof
关键字: keyof 代表一个类型的 key (用 | 连起来), 比如 keyof Person
keyof Person // 就相当于 'age' | 'name' | 'sex'
注意,这里的 extends 与平时看到的 extends 不是同一个意思
in
in 与 extends 的区别:
extends 是 |
in 是 &
K in keyof Person
中括号 [ ]
A[] // 意思是 A 组成的数组;
A["name"] // 意思是 A 类型里面的 "name" 属性的类型
&
且运算,一定要同时满足多个
|
或运算,可以满足一个或者多个
Readonly
Readonly VS readonly
Readonly
declare(未学习)
import & require
import XXX from './xxx.js' // xxx 的类型是 any
const XXX: string = require('./xxx.js') // 这个可以写类型
如果想要让 import 也有类型,需要创建一个同名的 .d.ts 文件:
declare function y(a: number, ba; number): number;
export default y
这样,上面的引入的东西,类型就不是 any 了
其它
类型兼容
interface Human {
name: string;
age: number;
}
let x: Human = {name: 'abc', age: 18, gender: 'male'} // 报错
let y = {name: 'abc', age: 18, gender: 'male'};
let x: Human = y; // 通过另外一个对象赋值过来,不会报错 ……
好处是,你可以少声明一些类型,不用特别声明一个 ……
soundness
我们在用函数的时候,有时会出现 类型不一致但是可以通过的想象,因为我给你的类型更加具体。
用 ts 添加 window 变量
declare global {
interface Window {
server: { // window.server
host: string
}
}
}
给 React 添加全局属性
declare module 'react' {
const xxx: number;
//一般我们也改不了 React 全局属性,不过可以在这里改:比如改(拓展) HTMLAttributes
interface HTMLAttributes extends AriaAttributes, DOMAttributes {
styleName ?: string // 在原来的基础上,添加这么一个
}
}