- Published on
TypeScript Basics & Basic Types
- Authors
- Name
- Jack Fan
TypeScript Basics & Basic Types
Using Types
一个简单的 JS 函数
const add = (a, b) => a + b
正常情况下,他返回两个数字的和,但如果有一个数字以字符串形式输入,返回的结果则会是字符串的拼接
console.log(add(5, 2.8)) // 7.8
console.log(add('5', 2.8)) // 52.8
使用 TS 在编写时就发现这个问题,而不是在编译的时候才发现问题。通过在参数后加:
,再加上其限定类型即可。
const add = (a: number, b: number) => a + b
console.log(add('5', 2.8)) // error
TypeScript Types vs JavaScript Types
JavaScript 中也有 typeof 操作符来检查参数的类型,就像这样
const add = (a: number, b: number) =>
typeof a !== 'number' || typeof b !== 'number' ? throw new Error('Incorrect input!') : a + b
这样同样可以做类型检查,但是这样很复杂,同时,依然无法在 Coding 的时候发现问题,只有当编译错误的时候,才会发现参数的类型错误,使用 TypeScript 规避这样的麻烦和错误,同时 TypeScript 拥有的类型也远远超过 JavaScript。
Working with Numbers, Strings & Booleans
现在来进一步扩充我们的 add
函数,添加一个参数 showResult
,类型为 boolean
,来决定函数执行完后是答应结果还是返回结果。再增加一个 phrase
参数,类型为 string
,如果要直接打印结果,前面想添加的输出内容作为 phrase 参数。
const add = (n1: number, n2: number, showResult: boolean, phrase: string) =>
showResult ? console.log(phrase + n1 + n2) : n1 + n2
const number1 = 5 // 5.0
const number2 = 2.8
const printResult = true
const resultPhrase = 'Result is: '
add(number1, number2, printResult, resultPhrase)
Type Assignment & Type Inference
TypeScript 代码会经过 Compiler 编译后变为绝大多数浏览器都可以理解的 JavaScript 代码。而且里面不带有类型推断,因为 JS 并没有这个能力。 TS 会对变量进行类型推断。 例如
const number = 5
如果鼠标放在 number 上,会看到显示 const number: 5
,这是因为 number 这个时候是个常量,他会把它看作就是 5。如果使用 let
声明呢?
let number: number
number = 5
let n = 5
鼠标放在 number 和 n 上,就会显示 let number: number
、let n: number
,因为我们声明 number
是一个 number 类型,同时 TypeScript 会推断 n
也是 number 类型。
n = '5'
number = '5'
这种行为会报错,因为我们已经声明这两个变量是 number
类型,不可以被赋值为 string 类型。
Object Types
对于 Object 如何处理?
const person = {
name: 'jack',
age: 30,
}
这是一个 object,可以使用点操作符访问内部键值对。鼠标放在 person
上,会出现它的类型。
const person: {
name: string
age: number
}
这看起来也像一个 object,但是不是,**每个键值对的分割使用的是';'而不是',',**而这就是 person 在 TS 里的类型。
如何确定类型呢
const person: object = {
//...
}
如果这样子赋类型,我们是无法使用点操作符访问内部对象的,typesript 会认为这只是一个最简单的 object 类型,就是 JavaScript 里的与 number、boolean 齐名的 object 类型。
const person: {
name: string
age: number
} = {
name: 'jack',
age: 30,
}
采用这种方式,对 object 里面每一个键的值声明清楚其类型即可。
Array Types
数组作为 JavaScript 里就存在的引用类型,也可以在 TypeScript 中做限制。
const person = {
name: 'jack',
age: 30,
hobbies: ['Sports', 'Cooking'],
}
这个时候去看 hobbies 的类型。显示的是 (property) hobbies: string[]
。这表明他是一个有 string 组成的 Array。 若要声明一个有 string 组成的 Array,可以这样
let favouriteActivites: string[]
favouriteActivites = ['Sports']
我们可以遍历这个数组看看
for (const hobby of person.hobbies) {
console.log(hobby.toUpperCase())
console.log(hobby.map()) //---> error: TS knows that the string type doesn't have the method 'map'
}
但是这样会报错,在遍历的时候,TypeScript 知道 hobbies 数组里面的值类型为 hobby,所以当我们在 hobby 下调用点操作符时,会为我们显示属于 string 的方法。但是 map
方法不会,他知道 string 类型是不会有 map
方法的
Working with Tuple
Tuple 是 TypeScript 提供的一种类型,他像是一个数组,但是它的长度,每一个位置的值的类型都是固定的。
const person: {
// ....
role: [number, string]
} = {
//....
role: [2, 'author'],
}
现在已经声明好了 role
作为一个 tuple,他长度只能是 2,第一个必须是 number 且第二个一定是 string。如下的代码会报错,因为类型不符合。同样的,超出长度的赋值也是会报错的。
person.role[1] = 10
person.role = [0, 'admin', 'user']
但是很可惜,对于 push
这样的操作,TypeScript 无法检测推入的长度是否会超出,以及推入的这一位,是否是这一位应该为的类型。(即通过 push
在第二个位置推入一个 number)。
person.role.push('admin')
Working with Enums
TypeScript 提供了枚举 Enum 类型。 在 JavaScript 中会遇到一些问题,给一些东西赋固定值的时候,会输入错具体的值,使得程序无法正常运行,如下:
const person = {
role: 'READ ONLY',
}
if (person.role === 'READ-ONLY') console.log('Read only.')
一个字符之差,导致这条信息永远不被输出,所以我们有其他的解决方案
const ADMIN = 'ADMIN'
const READ_ONLY = 'READ_ONLY'
const person = {
role: READ_ONLY,
}
if (person.role === READ_ONLY) console.log('Read only.')
这可以规避上述的错误,但依然略显繁琐,可以使用 TypeScript 的 enmu 类型。
enum Role {
ADMIN,
READ_ONLY,
AUTHOR,
}
const person = {
// ...
role: Role.ADMIN,
}
if (person.role === Role.ADMIN) console.log('I am Admin')
这样又方便而且不会出错,类型分明。同时,如果把鼠标移到 Role 里每个项的上,可以看到他们的具体值。被分别按顺序赋予了 1、2、3。 如果想要改变他们的初始值也可以,例如
enum Role {
ADMIN = 200,
READ_ONLY,
AUTHOR,
}
这样 ADMIN 的值会是 200,剩余两个也是以此增加,分别为 201 和 202。 当然也可以赋予其他的类型。
enum Role {
ADMIN = 'ADMIN',
READ_ONLY = 100,
AUTHOR = 'AUTHOR',
}
这同样可行。
The any Type
any 告诉 TypeScript 这个值的类型可以为任何,例如
const a = any[];
这个数组里面的值可以是任何类型,没有限制。 尽量避免使用 any,TypeScript 没有办法对他做任何检查,any 用的多和写 JavaScript 没有区别
Union Types
有时候我们希望我们的函数有更多的功能,接收的参数类型不同,返回的结果也不同,这个时候可以使用 Union Type
const combine = (a: number | string, b: number | string) =>
typeof a === 'number' && typeof b === 'number' ? a + b : a.toString() + b.toString
const combineAges = combine(30, 26)
console.log(combineAges)
const combineNames = combine('Du', 'Lang')
console.log(combineNames)
在这个例子中,combine 函数的参数接收两种类型的参数,string 和 number,如果两个参数都为 number 类型,那就对他们做简单的相加,否则就做及简单的字符串拼接。 使用 Union type 我们限制了参数的类型的同时,扩充函数的功能
Literal Types
Literal type 为写死的内容。例如之前的
const number = 5
查看它的类型会发现它就只写了一个 5,我们可以利用这个特性。
const combine = (
a: number | string,
b: number | string,
resultConversion: 'as-number' | 'as-text'
) =>
(typeof a === 'number' && typeof b === 'number') || resultConversion === 'as-number'
? +a + +b
: a.toString() + b.toString
const combineAges = combine(30, 26, 'as-number')
console.log(combineAges)
const combineNames = combine('Du', 'Lang', 'as-text')
console.log(combineNames)
这个时候,第三个参数 resultConvension 的值就只能为二者其一,被严格限定,避免出现错误。 如果传的参数或者在函数内判断其的值不为这二者其一,TypeScript 都会报错,从而规避错误。
Type Aliases Custom Types
类型别名,使用 type 操作符定义。
type Combinable = number | string
type ConversionDescriptor = 'as-number' | 'as-text'
const combine = (a: Combinable, b: Combinable, resultConversion: ConversionDescriptor) =>
(typeof a === 'number' && typeof b === 'number') || resultConversion === 'as-number'
? +a + +b
: a.toString() + b.toString
如果我们的参数类型复杂,这样会更清晰,同时也更能明确为什么是这些类型。也可以编码更加复杂的类型定义。并且有着良好的复用性。
Function Return Types && void
我们可以声明 function 的返回类型。先来看看
const add = (a: number, b: number) => a + b
鼠标放大 add 上 会显示 const add: (a: number, b: number) => number
,箭头后面的 number 表面它会返回一个 number 类型的值,这是由 TypeScript 自己推断出来的。我们也可以显式的声明
const add = (a: number, b: number): number => a + b
也是通过 :
来做声明,如果你不知道返回类型究竟是什么,让 TypeScript 自己推断就好。
对于不返回东西的 function,也提供了 void 类型,就和其它语言一样。
const printResult = (n: number): void => console.log('Result: ' + n)
这里即使不写清楚 void,TypeScript 也会自己推断出这是 void 类型。 还有另一种返回类型,undefined,他会写 return 语句但是不不返回任何东西
function returnUndefined(): undefined {
return
}
Functions as Types
我们有时候也会希望函数本身可以作为一种类型,或者特定的一种函数,作为一种类型。
const add = (a: number, b: number): number => a + b
let combineFunction
combineFunction = add
combineFunction
的类型是 type 是 any,所以可以赋值为 add。 我们也可以限定其类型为 Function。
let combineFunction: Funtcion
但这样太宽泛了,它可以被赋值为任意一个函数都可以。可以进一步限制。
const add = (a: number, b: number): number => a + b
const printResult = (n: number): void => console.log('Result: ' + n)
let combineFunction: (a: number, b: number) => number
combineFunction = add
// combineFunction = printResult;
这个时候,combineFunction
就只可以被赋值为 add 了,因为他接受两个参数,类型为 number,且返回一个 number 值。
Functions Types & Callback
有时候需要传入回调函数,我们也可以对回调函数,作出一定的限制
const add = (a: number, b: number, callback: (n: number) => number): number => callback(a + b)
add(1, 2, (num) => num * 10)
这样我们就限定了,回到函数的类型,参数类型,和返回类型。
Unknown Type
如果不知道你要储存的变量的类型,但你知道你最终要用他做什么,使用 unknown
类型
let userInput: unknown
let userName: string
userInput = 5
userInput = 'Max'
if (typeof userInput === 'string') userName = userInput
可以看到,unknown 类型可以被赋值为任何值,类似 unknown。但不能随意给其他变量赋值,可以在判断类型后赋值,否则会报错。 同样,尽量不要用 unknown 类型,如果知道有哪些类型,union 是更好的选择。
Never Type
还有一种返回类型,never。他表示从不返回任何值,例如抛出异常或者其他的等等。
function generateError(msg: string, code: number): never {
throw { msg, code }
}
generateError('An error', 500)