TypeScript vs JavaScript
TypeScript 是"强类型"版的JavaScript,当我们在代码中定义变量(普通变量、函数、组件、hook)等的时候,TypeScript 允许我们在定义的同时指定其类型,这样使用者在使用不当或不规范的时候就会被及时报错提醒
TypeScript 的几种类型
1.number
数字类型,包括小数、其它进制的数字:
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;
2.string
字符串
let name:string = "vanYu";
3.array
在 TS 中,array 一般指所有元素类型相同的集合,比如:
let list:Array<number> = [1,2,3]
// or
interface User {
name:string
}
const vanYu = {name:'vanYu'}
const Avan = {name:'Avan'}
let personList = [vanYu,Avan] // 这里 vanYu 和 Avan 都是 User 类型的
和这种混合类型的"数组":
let mList = ['vanYu',10]
注意:在 TS 中不是 数组/array,它们叫做 tuple,下面会提到
4.boolean
布尔值
let isDone:boolean = false
5.函数
两种方法 1.在我们熟悉的"JS函数"上直接声明参数和返回值
const isFalsy = (value:any):boolean => {
return value ===0 ? true : !!value
}
2.直接声明你想要的的函数类型:
export const useMount = (fn: () => void) => {
useEffect(() => {
fn();
}, []);
};
const isFalsy: (value: any) => boolean = (value) => {
return value === 0 ? true : !!value;
};
6. any
any 表示这个值可以是任何值,被定义为 any 就意味着不做任何类型检查
let looselyTyped: any = 4;
// looselyTyped 的值明明是个4,哪里来的ifItExists方法呢?
// 由于声明为any,我们没法在静态检查阶段发现这个错误
looselyTyped.ifItExists();
初学 TS 的童鞋经常会为了让 TS 不再报错就用了很多 any,这样做会失去 TS 的保护。童鞋们应该尽量避免使用 any
7. unknown
unknown 表示这个值可以是任何值
有童鞋可能会问,这和 any 有什么区别呢?
unknown 的用法:在你想用 any 的时候,用 unknown 来代替,简单来说,unknown是一个"严格"版的 any
const isFalsy = (value: unknown) => {
// 大家不用考虑这段console有啥意义,把它打在你的代码里对应的位置,观察编辑器会不会报错;
// 再思考它应不应该报错
console.log(value.mayNotExist)
return value === 0 ? true : !!value;
};
8. void
void 类型绝大部分情况下,只会用在这一个地方:表示函数不返回任何值或者返回undefined (因为函数不返回任何值的时候 === 返回 undefined)
const doSomething = (callback: () => void) => {
let c = callback() // 在这个位置,回调总是返回 undefined //c 也是 undefined 类型
}
9. object
除了 number, string, boolean, bigint, symbol, null, or undefined,其它都是 object
10. tuple
tuple 是 "数量固定,类型可以各异" 版的数组
let x: [string, number];
x = ["hello", 10]; // OK
x = [10, "hello"]; // Error
// 可以定义复杂的tuple
type NestedTuple = [string, Array<number>, [boolean, {name: string}]]
let testTuple: NestedTuple = ['1', [1, 2, 3], [true, { name: 'vanYu' }]]; // ok
11.enum
枚举(enum) 类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等
1.基础用法
enum Color {Red, Green, Blue}
let c: Color = Color.Red;
let d: Color = Color.Green;
let e: Color = Color.Blue;
console.log(c,d,e) //0,1,2
2.设置初始值
enum Color {Red, Green=2, Blue,Yellow=7 ,Dark}
let c: Color = Color.Red;
let d: Color = Color.Green;
let e: Color = Color.Blue;
let f: Color = Color.Yellow;
let g: Color = Color.Dark;
console.log(c,d,e,f,g) //0 2 3 7 8
3.属性获取
enum Color {Red, Green=2, Blue,Yellow=7 ,Dark}
let c1: string = Color[0];
let c: Color = Color.Red;
let d1: string = Color[1];
let d: Color = Color.Green;
let e1: string = Color[2];
let e: Color = Color.Blue;
let f1: string = Color[3];
let f: Color = Color.Yellow;
let g1: string = Color[4];
let g: Color = Color.Dark;
console.log(c1,c,d1,d,e1,e,f1,f,g1,g) //Red 0 undefined 2 Green 3 Blue 7 undefined 8
// 属性获取时按照属性对应的值来获取的,所以会有 undefined
4.应用场景
// 后端返回的字段使用 0 - 6 标记对应的日期,这时候就可以使用枚举可提高代码可读性
// 包括后端日常返回0、1 等等状态的时候,我们都可以通过枚举去定义,这样可以提高代码的可读性,便于后续的维护
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
12. null 和 undefined
null 和 undefined 在 TypeScript 中既是一个值,也是一个类型:
let u: undefined = undefined;
let n: null = null;
13. never
详细解释和用法可参考尤大的博客www.zhihu.com/question/35…
// 这个 func返回的就是never类型,用到比较少,在类型操作等场景会用到
const func = () => {
throw new Error()
}
泛型
泛型 简单来说就是类型变量
泛型 可以用来创建重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件
T代表 Type,在定义泛型时通常作第一个类型变量名称。但实际上可以用任何有效名称代替。除了T之外,以下是常见泛型变量代表的意思:
- K(Key): 表示对象中键类型;
- V(Value): 表示对象中值类型;
- E(Element): 表示元素类型;
1.基本使用
// 这里`identity`方法可以将`number`类型分配给参数和返回类型,使该函数仅可用该原始类型。但该函数并不是可扩展或通用的。
// 如果我们把`number`换成了`any`,那么就是去了定义返回哪种类型的能力,也会是去编译器类型保护的作用
function identity(value:number):number{
return value;
}
console.log(identity(1)) // 1
// 为了实现任何特定的类型,我们可以使用泛型来解决这个问题,具体实现如下:
function identity<T>(value:T):T{
return value;
}
console.log(identity<number>(1)) // 返回1
console.log(identity(1)) // 更常见的做法是省略尖括号内的类型变量,编译器会自动选择类型 返回1
2.进阶使用
// 定义多个类型变量 引入希望定义的任何数量的类型变量
function identity <T, U>(value: T, message: U) : T {
console.log(message);
return value;
}
console.log(identity<number, string>(68, "Semlinker"));
/* 相比之前定义的 `identity` 函数,新的 `identity` 函数增加了一个类型变量 `U`,
但该函数的返回类型我们仍然使用 `T`。如果我们想要返回两种类型的对象该怎么办呢?
针对这个问题,我们有多种方案,其中一种就是使用元组,即为元组设置通用的类型:*/
function identity <T, U>(value: T, message: U) : [T, U] {
return [value, message];
}
3.泛型接口
interface Identities<V,M>{
value: v,
message: M
}
/*在上述的`Identities`接口中,我们引入了类型变量`V`和`M`,进一步说明有效的字母都可以用于表示类型变量,
之后我们就可以将`Identities`接口作为`identity`函数的返回类型:*/
function identity<T, U> (value: T, message: U): Identities<T, U>{
console.log(value + ": " + typeof (value));
console.log(message + ": " + typeof (message));
let identities: Identities<T, U> = {
value,
message
};
return identities;
}
console.log(identity(68, "Semlinker"));
// 68: number
// Semlinker: string
// {value: 68, message: "Semlinker"}
interface
interface 不是一种类型,应该被翻译成 接口,或者说使用上面介绍的类型,创建一个我们自己的类型
interface User {
id: number;
}
const u: User = {id: 1}
啥时候需要声明类型
理论上来说在我们声明任何变量的时候都需要声明类型(包括普通变量、函数、组件、hook等等),声明 函数、组件、hook 等需要声明参数 和 返回值的类型。
但是在很多情况下,TS可以帮我们自动推断,我们就不用声明了,比如:
// 这里虽然没有显式声明,但是ts自动推断这是个number
let a = 1
// 自动推断返回值为number
function add(a: number, b: number) {
return a + b;
}
// 自动推断返回值为boolean
const isFalsy = (value: unknown) => {
return value === 0 ? true : !!value;
};
.d.ts
JS 文件 + .d.ts 文件 === ts 文件
.d.ts 文件可以让 JS 文件继续维持自己JS文件的身份,而拥有TS的类型保护
一般我们写业务代码不会用到,但是点击类型跳转一般会跳转到 .d.ts文件