在安装 Typescript 之前,需要先安装 npm,然后可以使用命令行来安装 Typescript,如下所示:
npm install typescript@next
在npm install typescript@next
命令中,@next
表示安装 TypeScript
的下一个版本,也就是最新的开发版本或预发布版本。
通常情况下,npm install typescript
会安装最新的稳定版本的 TypeScript
。但是,如果我们想尝试最新的功能或修复了一些 bug
的预发布版本,可以使用@next
标记来安装这个版本。
预发布版本通常是在正式发布之前进行测试和反馈的版本。它们可能包含一些新的功能、改进或修复了一些问题,但也可能存在一些不稳定性或兼容性问题。因此,使用@next
标记安装预发布版本时,需要注意这些潜在的问题,并确保我们的代码和依赖项与该版本兼容。
现在局部安装的 Typescript
将是最新版本,而且各种IDE也支持它。例如,我们可以使用 VS Code
通过创建 .vscode/setting.json
来使用这个版本,如下所示:
{
"typescript.tsdk":"./node_modules/typescript/lib"
}
在 .vscode/settings.json
文件中设置 "typescript.tsdk":"./node_modules/typescript/lib"
的作用是告诉 Visual Studio Code(VS Code)
使用指定的 TypeScript SDK(软件开发工具包)
路径。
TypeScript SDK
是一组用于开发和构建 TypeScript
项目的工具和库。当你在 VS Code
中使用 TypeScript
时,它需要知道 TypeScript SDK
的位置,以便提供语法检查、代码补全、类型检查等功能。
通过在 .vscode/settings.json
文件中设置 "typescript.tsdk"
属性,你可以指定 TypeScript SDK
的路径。在这个例子中,"./node_modules/typescript/lib"
表示 TypeScript SDK
的路径是当前项目
的 node_modules
目录下的 typescript/lib
文件夹。
这样设置后,VS Code
将使用指定路径下的 TypeScript SDK
来提供相关的功能和工具。
需要注意的是,这个设置是针对特定项目的,因此它会覆盖全局的 TypeScript SDK
设置。这样可以确保每个项目都使用其自己的 TypeScript SDK
版本,而不会受到全局设置的影响。
类型是出色的文档形式之一:
类型信息可以帮助我们了解变量、函数参数和返回值的预期数据结构。在阅读和理解代码时,类型信息可以作为一种隐式文档,使我们更容易地推断代码的功能和行为。此外,类型信息还可以在编译时提供类型检查,帮助我们捕获潜在的错误。
例如,考虑以下函数:
function add(a: number, b: number): number {
return a + b;
}
通过查看类型信息,我们可以快速地了解这个函数的功能:它接受两个数字参数 a
和 b
,并返回它们的和。这种类型信息可以作为一种简洁、易于理解的文档形式。
函数签名是一个定理:
函数签名
定义了函数的 输入参数类型
、 返回值类型
以及 可能抛出的异常
。它描述了函数的约束和预期行为,就像数学定理一样。函数签名可以帮助我们确保函数的实现和调用遵循预期的类型约束,从而提高代码的可读性和可维护性。
例如,考虑以下函数签名:
type MyFunctionType = (param1: string, param2: number) => boolean;
这个函数签名定义了一个定理:存在一个函数,它接受一个字符串参数 param1
和一个数字参数 param2
,并返回一个 布尔值
。这个定理描述了函数的约束和预期行为。
函数是具体的实现:
函数体是函数签名(定理)的具体实现。它描述了如何根据输入参数计算返回值。函数体需要遵循函数签名的约束,以确保代码的正确性和一致性。
例如,考虑以下函数实现:
const myFunction: MyFunctionType = (param1, param2) => {
return param1.length > param2;
};
这个函数体实现了之前定义的函数签名(定理)。它根据输入参数 param1
和 param2
计算了一个 布尔值
,即 param1
的长度是否大于 param2
。
总之,类型、函数签名和函数体在编程中起着重要作用。类型信息可以作为一种出色的文档形式,函数签名定义了函数的约束和预期行为(定理),而函数体提供了具体的实现。这些概念有助于提高代码的可读性、可维护性和健壮性。
鸭子类型(Duck Typing)
是一种动态类型语言的编程概念,它关注对象的行为,而不是对象的实际类型。换句话说,如果一个对象像鸭子一样走路、叫声,那么我们就可以把它当作鸭子来对待,而不关心它是否真的是鸭子。在鸭子类型的语言中,函数和方法的参数不需要声明具体的类型,而是通过对象的属性和方法来判断它是否符合预期的行为。
鸭子类型的优势在于它提供了很高的灵活性和可扩展性。由于鸭子类型关注对象的行为,而不是类型本身,这使得我们可以编写更通用、更易于重用的代码。这也是为什么鸭子类型被认为是一流的语言结构。
以下是一个简单的例子,说明了鸭子类型的概念:
function makeSound(animal) {
console.log(animal.sound());
}
const duck = {
sound: () => "Quack!",
};
const dog = {
sound: () => "Woof!",
};
makeSound(duck); // 输出 "Quack!"
makeSound(dog); // 输出 "Woof!"
在这个例子中,makeSound
函数接受一个 animal
参数,但我们没有声明它的具体类型。相反,我们依赖于 animal
对象是否具有 sound
方法来判断它是否符合预期的行为。这使得我们可以将不同类型的对象传递给 makeSound
函数,只要它们具有 sound
方法即可。
需要注意的是,鸭子类型主要适用于动态类型语言(如 JavaScript
、Python
等)。在静态类型语言(如 TypeScript
、Java
等)中,鸭子类型的概念仍然存在,但通常需要通过接口或其他类型抽象机制来实现。例如,在 TypeScript
中,我们可以使用 接口
来实现类似的效果:
interface Animal {
sound(): string;
}
function makeSound(animal: Animal) {
console.log(animal.sound());
}
const duck: Animal = {
sound: () => "Quack!",
};
const dog: Animal = {
sound: () => "Woof!",
};
makeSound(duck); // 输出 "Quack!"
makeSound(dog); // 输出 "Woof!"
在这个例子中,我们定义了一个 Animal
接口,它要求实现 sound
方法。makeSound
函数接受一个 Animal
类型的参数,这使得我们可以传递任何实现了 Animal
接口的对象。尽管 TypeScript
是静态类型的,但通过接口,我们仍然可以实现鸭子类型的灵活性。
Typescript
的主要目标之一是让人们能够安全、轻松地在 Typescript
中使用现有的 Javascript
库。它可以通过声明来做到这一点。Typescript
提供了一个浮动的标准来衡量你希望在声明中投入多少努力;投入的越多,你获得的类型安全和代码智能提示就越多。
请注意,大多数流行的 Javascript
库的声明定义已经由 DefinedTyped
社区编写过了,因此,在大多数情况下,声明文件已经存在;或者,至少已经拥有了大量经过深思熟虑的可用的 Typescript
声明目标。
可以把 jQuery
作为一个编写声明文件的简单示例。在默认情况下,Typescript
要求(正如良好的 Javascript
代码所要求的一样)在使用变量(即在某处使用 var
)之前先进行声明。
在 Typescript
中,直接编写下面代码会报错:
$(".awesome").show(); // 错误,找不到$
$(".awesome").show();
是使用 jQuery
库中的 选择器
来选取所有具有 awesome
类名的元素,并将它们显示出来。
具体作用如下:
$
符号是 jQuery
库的入口函数,它用于选取元素或创建 jQuery
对象。$(".awesome")
使用 CSS
选择器 .awesome
来选取所有具有 awesome
类名的元素。这个选择器会匹配文档中所有类名为 awesome
的元素。.show()
是 jQuery
提供的一个方法,用于显示被选元素。它会将选中的元素设置为可见状态,如果元素之前被隐藏了。因此,$(".awesome").show();
的作用是将所有具有 awesome
类名的元素显示出来,使它们在页面上可见。
在 TypeScript
中,$(".awesome").show();
会报错,因为 TypeScript
是一种强类型语言,它需要知道变量和对象的类型。在这个例子中,$
是一个全局变量,它代表 jQuery
库。TypeScript
编译器不知道 $
的类型,因此会抛出一个错误。
要修复这个错误,需要在 TypeScript
代码中声明 $
的类型。这可以通过添加 declare var \$: any;
这一行代码来实现。这告诉 TypeScript
编译器,$
是一个任意类型的变量,因此编译器不会对其进行类型检查。如下所示:
declare var $: any;
$(".awesome").show();
这样修改之后,TypeScript
编译器就可以正确地识别 $
变量,不再报错。但是,使用 any
类型会导致失去类型安全性,因此在实际项目中,建议使用 @types/jquery
这样的类型定义库,而不是直接使用 any
类型。安装 @types/jquery
后,可以直接使用 $
变量,而不需要 declare var \$: any;
这一行代码。
如果你愿意,可以基于此来定义结构,并提供更多信息以保护你免收错误的影响,如下所示:
declare var $: {
(selector: string): any;
}
$(".awesome").show(); // 正确;
$(123).show(); // 错误,selector 必须为 string 类型的
在这段代码中,我们声明了 $
变量的类型为一个对象,这个对象包含一个函数签名,表示这个函数接受一个 string
类型的参数 selector
,并返回 any
类型的值。
declare var $: {
(selector: string): any;
};
接下来,我们调用了 $
函数两次:
$(".awesome").show();
$(123).show();
第一次调用是正确的,因为我们传递了一个字符串参数 ".awesome"
。然而,第二次调用是错误的,因为我们传递了一个数字参数 123
。根据我们之前声明的类型,$
函数只接受一个 string
类型的参数。所以,当我们尝试传递一个 number
类型的参数时,TypeScript
编译器会报错,因为这不符合我们声明的类型约束。
要修复这个错误,我们需要确保传递给 $
函数的参数是一个字符串。例如,我们可以将数字转换为字符串,如下所示:
$(String(123)).show();
或者,我们可以修改 $
函数的类型声明,使其接受 number
类型的参数。但是,在实际的 jQuery
用法中,通常不会使用数字作为选择器,因此这种修改可能并不合适。
函数签名是一种定义函数类型的语法,它描述了函数的输入参数类型、返回值类型以及可能抛出的异常。在 TypeScript
中,函数签名通常用于定义接口、类型别名或对象类型中的函数属性。函数签名可以帮助我们确保函数的实现和调用遵循预期的类型约束,从而提高代码的可读性和可维护性。
函数签名的基本语法如下:
(param1: Type1, param2: Type2, ...): ReturnType;
param1
、param2
等是函数的参数名;Type1
、Type2
等是函数参数的类型;ReturnType
是函数的返回值类型;以下是一些函数签名的用法示例:
在接口中定义函数签名:
interface MyInterface {
myFunction(param1: string, param2: number): boolean;
}
这个接口 MyInterface
包含一个名为 myFunction
的函数,它接受一个 string
类型的参数 param1
和一个 number
类型的参数 param2
,并返回一个 boolean
类型的值。
在类型别名中定义函数签名:
type MyFunctionType = (param1: string, param2: number) => boolean;
这个类型别名 MyFunctionType
描述了一个函数,它接受一个 string
类型的参数 param1
和一个 number
类型的参数 param2
,并返回一个 boolean
类型的值。
在对象类型中定义函数签名:
type MyObjectType = {
myFunction(param1: string, param2: number): boolean;
};
这个对象类型 MyObjectType
包含一个名为 myFunction
的函数,它接受一个 string
类型的参数 param1
和一个 number
类型的参数 param2
,并返回一个 boolean
类型的值。
定义可选参数和默认参数:
type MyFunctionType = (param1: string, param2?: number, param3: boolean = true) => boolean;
在这个函数签名中,param2
是一个可选参数,它的类型为 number | undefined
。param3
是一个具有默认值 true
的参数,它的类型为 boolean
。
定义剩余参数
type MyFunctionType = (param1: string, ...restParams: number[]) => boolean;
在这个函数签名中,...restParams
表示一个剩余参数,它是一个 number
类型的数组。这意味着该函数可以接受一个 string
类型的参数 param1
和任意数量的 number
类型的参数。
TypeScript
的箭头函数 (Arrow Function)
是 ES6(ECMAScript 2015)
引入的一种新的函数语法,它使用 =>
符号表示。箭头函数具有更简洁的语法,同时它自动绑定了上层作用域的 this
值,这使得在某些场景下编写函数更加方便。箭头函数在 TypeScript
中的使用和在 JavaScript
中的使用基本相同,只是 TypeScript
会对参数和返回值进行类型检查。
以下是箭头函数的一些特点和用法:
简洁的语法:
箭头函数的语法比传统的函数表达式更简洁。例如,一个简单的箭头函数如下所示:
const add = (a: number, b: number): number => a + b;
这个箭头函数接受两个 number
类型的参数 a
和 b
,并返回它们的和。等效的传统函数表达式如下所示:
const add = function (a: number, b: number): number {
return a + b;
};
自动绑定 this
值:
箭头函数会自动捕获它所在上下文的 this
值。这使得在某些场景下编写函数更加方便,例如在类方法中处理事件回调。例如:
class MyClass {
value: number;
constructor(value: number) {
this.value = value;
}
onClick() {
// 使用箭头函数自动捕获 `this` 值
document.addEventListener("click", () => {
console.log(this.value);
});
}
}
在这个例子中,onClick
方法中的箭头函数自动捕获了 this
值,使得我们可以在事件回调中访问 MyClass
实例的 value
属性。如果使用传统的函数表达式,我们需要手动绑定 this
值,例如通过 bind
方法。
class MyClass {
value: number;
constructor(value: number) {
this.value = value;
}
onClick() {
// 使用传统的函数表达式,并通过 `bind` 方法手动绑定 `this` 值
document.addEventListener("click", function () {
console.log(this.value);
}.bind(this));
}
}
在这个例子中,onClick
方法中的传统函数表达式使用了 bind
方法将 this
值绑定到 MyClass
实例。这使得我们可以在事件回调中访问 MyClass
实例的 value
属性。这种方法在 ES6
引入箭头函数之前,是 JavaScript
中常用的处理 this
值的方法。
没有 arguments
对象:
箭头函数没有自己的 arguments
对象。它会访问其外部作用域的 arguments
对象。例如:
function outerFunction(a: number, b: number) {
const add = (): number => {
// 访问外部作用域的 `arguments` 对象
return arguments[0] + arguments[1];
};
return add();
}
在这个例子中,箭头函数 add
访问了 outerFunction
的 arguments
对象。请注意,在 TypeScript
中,arguments
对象的类型安全性较差,因此在实际项目中,建议避免使用 arguments
对象,而是使用具名参数或剩余参数 (...rest)
。
总之,TypeScript
的箭头函数提供了更简洁的语法和自动绑定 this
值的特性,使得编写函数更加方便。但请注意,箭头函数并不适用于所有场景,例如在定义类方法或需要使用 prototype
的情况下,仍然需要使用传统的函数表达式或方法定义。