1.TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准(ES6 教程)。
2.TypeScript 由微软开发的自由和开源的编程语言。
3.TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
4.2012年10月,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript
5.TypeScript的作者是安德斯·海尔斯伯格,C#的首席架构师。它是开源和跨平台的编程语言。
6.TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。
7.TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。
8.TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6+ 的支持**,它由 Microsoft 开发,代码开源于 GitHub 上
编程语言按照数据类型大体可以分为两类,一类是静态类型语言,另一类是动态类型语言。
静态类型语言在编译时便已确定变量的类型,而动态类型语言的变量类型要到程序运行的时候,待变量被赋予某个值之后,才会具有某种类型。在 JavaScript 语言中,只有当我们对一个变量赋值时才会最终确定这个变量的数据类型,因此,JavaScript 是一门典型的动态类型语言。
动态类型的自由特性经常会导致错误,这些错误不仅会降低程序员的工作效率,而且还会由于增加新代码行,使得成本增加,最终开发陷入停顿。因为 JavaScript 无法合并类型以及编译时缺乏错误检查,因此,它不适合作为企业和大型代码库中服务器端代码。而 TypeScript 类型的定义和编译器的引入可以避免这些错误的出现。
一项研究表明,TypeScript 可以检测到所有 JavaScript 错误的 15%。在不严重破坏代码的情况下,开发人员可以更轻松地避免错误并进行代码重构。
总而言之,与 JavaScript 相比,TypeScript 代码更可靠、更容易重构。
显式类型使我们的代码可读性更高,所以我们的注意力将会更集中在系统究竟如何构建,以及系统的不同部分如何相互作用上。目前,JavaScript 已经成为了 Web 应用程序的主要开发语言,但是在大型复杂的 Web 应用系统中,JS 代码会显得杂乱无章难以梳理且调试困难,而 TypeScript 的类型定义使我们可以在大型、复杂的应用程序中,编写出更清晰的代码,源码的阅读也更容易更清晰。
同时,由于 JavaScript 是 TypeScript 的子集,因此可以在 TypeScript 代码中使用您想要的所有 JavaScript 库和代码。而且与其他语言不同,TypeScript 和 JavaScript 的语法并没有明显的区别,这就意味着你无需学习很多知识就可以开始无缝编写TypeScript 代码。
总结一下:
TypeScript 让我们的代码更可靠更清晰。
TypeScript 是 JavaScript 超集,完全兼容所有 JavaScript 语法规则。
TypeScript 可以编译出纯净、 简洁的 JavaScript 代码,并且可以运行在任何浏览器上、Node.js 环境中和任何支持 ECMAScript 3(或更高版本)的JavaScript 引擎中。
类型系统允许 JavaScript 开发者在开发 JavaScript 应用程序时使用高效的开发工具和常用操作比如静态检查和代码重构。
TypeScript 提供最新的和不断发展的 JavaScript 特性,包括那些来自 2015 年的 ECMAScript 和未来的提案中的特性,比如异步功能和 Decorators,以帮助建立健壮的组件。
#总结
命令行运行如下命令,全局安装 TypeScript:
npm install -g typescript
安装完成后,在控制台运行如下命令,检查安装是否成功(3.x):
tsc -V
src/helloworld.ts
function greeter (person) {
return 'Hello, ' + person
}
let user = 'Yee'
console.log(greeter(user))
我们使用了 .ts 扩展名,但是这段代码仅仅是 JavaScript 而已。
在命令行上,运行 TypeScript 编译器:
tsc helloworld.ts
输出结果为一个 helloworld.js 文件,它包含了和输入文件中相同的 JavsScript 代码。
在命令行上,通过 Node.js 运行这段代码:
node helloworld.js
控制台输出:
Hello, Yee
(1) 生成配置文件tsconfig.json
tsc --init
(2) 修改tsconfig.json配置
"outDir": "./js",
"strict": false,
(3) 启动监视任务:
终端 -> 运行任务 -> 监视tsconfig.json
接下来让我们看看 TypeScript 工具带来的高级功能。 给 person 函数的参数添加 : string 类型注解,如下:
function greeter (person: string) {
return 'Hello, ' + person
}
let user = 'Yee'
console.log(greeter(user))
TypeScript 里的类型注解是一种轻量级的为函数或变量添加约束的方式。 在这个例子里,我们希望 greeter 函数接收一个字符串参数。 然后尝试把 greeter 的调用改成传入一个数组:
function greeter (person: string) {
return 'Hello, ' + person
}
let user = [0, 1, 2]
console.log(greeter(user))
重新编译,你会看到产生了一个错误:
error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
类似地,尝试删除 greeter 调用的所有参数。 TypeScript 会告诉你使用了非期望个数的参数调用了这个函数。 在这两种情况中,TypeScript提供了静态的代码分析,它可以分析代码结构和提供的类型注解。
要注意的是尽管有错误,greeter.js 文件还是被创建了。 就算你的代码里有错误,你仍然可以使用 TypeScript。但在这种情况下,TypeScript 会警告你代码可能不会按预期执行。
接口是对传入参数进行约束;或者对类里面的属性和方法进行声明和约束,实现这个接口的类必须实现该接口里面属性和方法;typescript中的接口用interface关键字定义。
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。 TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
让我们继续扩展这个示例应用。这里我们使用接口来描述一个拥有 firstName 和 lastName 字段的对象。 在 TypeScript 里,只在两个类型内部的结构兼容,那么这两个类型就是兼容的。 这就允许我们在实现接口时候只要保证包含了接口要求的结构就可以,而不必明确地使用 implements 语句。
interface Person {
firstName: string
lastName: string
}
function greeter (person: Person) {
return 'Hello, ' + person.firstName + ' ' + person.lastName
}
let user = {
firstName: 'Yee',
lastName: 'Huang'
}
console.log(greeter(user))
interface Person {
name: string
age: number
}
let tom: Person = {
name: 'Tom',
age: 25
}
上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。 接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀。 定义的变量比接口少了一些属性是不允许的:
interface Person {
name: string
age: number
}
let tom: Person = {
name: 'Tom'
}
多一些属性也是不允许的:
interface Person {
name: string
age: number
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
}
// Type '{ name: string age: number gender: string }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
可见, 赋值的时候,变量的形状必须和接口的形状保持一致。
有时我们希望不要完全匹配一个形状,那么可以用可选属性:
interface Person {
name: string
age?: number
}
let tom: Person = {
name: 'Tom'
}
interface Person {
name: string
age?: number
}
let tom: Person = {
name: 'Tom',
age: 25
}
这时仍然不允许添加未定义的属性:
interface Person {
name: string
age?: number
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
}
// Type '{ name: string age: number gender: string }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
有时候我们希望一个接口允许有任意的属性,可以使用如下方式:
interface Person {
name: string
age?: number
[propName: string]: any
}
let tom: Person = {
name: 'Tom',
gender: 'male'
}
使用 [propName: string] 定义了任意属性取 string 类型的值。 需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
interface Person {
name: string
age?: number
[propName: string]: string
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
}
// Property 'age' of type 'number | undefined' is not assignable to string index type 'string'.
// Type '{ name: string age: number gender: string }' is not assignable to type 'Person'.
// Property 'age' is incompatible with index signature.
// Type 'number' is not assignable to type 'string'.
上例中,任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了。
另外,在报错信息中可以看出,此时 { name: ‘Tom’, age: 25, gender: ‘male’ } 的类型被推断成了 { [x: string]: string | number name: string age: number gender: string },这是联合类型和接口的结合。
有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性:
interface Person {
readonly id: number
name: string
age?: number
[propName: string]: any
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
}
tom.id = 9527
// Cannot assign to 'id' because it is a read-only property.
上例中,使用 readonly 定义的属性 id 初始化后,又被赋值了,所以报错了。
注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候:
interface Person {
readonly id: number
name: string
age?: number
[propName: string]: any
}
let tom: Person = {
name: 'Tom',
gender: 'male'
}
tom.id = 89757
// Property 'id' is missing in type '{ name: string gender: string }' but required in type 'Person'.
// Cannot assign to 'id' because it is a read-only property.
对传入对象的约束,也就是json数据
interface Sx {
name : string
age : number
}
function f8(peop:Sx) {
//name age 必须传递
console.log(peop)
}
const obj = {
name : 'liu',
age : 25
}
对方法传入的参数和返回值进行约束
interface Sta {
(difang : string, todo : string) : string
}
let play : Sta = (difang:string, todo:string) : string => {
return `我们去${difang}吃${todo}`
}
console.log(play('灞桥', '吃烧烤'))
对索引和传入的参数的约束
//对数组的约束
interface UserArr {
//索引为number,参数为string
[index : number] : string
}
const arr : UserArr = ['a', 'b']
console.log(arr)
//对 对象的约束
interface UserObj {
[index : number] : number
}
const obj1 : UserObj = { 2:1, 3:4 }
console.dir(obj1)
//父类Anmal看上面
//实现LaoHu的这个接口,必须也要实现LaoHu继承的Anmal接口中的方法
interface LaoHu extends Anmal{
say (sa : string) : void
}
//继承并实现接口
class XiaoLaoHu implements LaoHu{
name : string
constructor (name : string) {
this.name = name
}
eat (food : string) : void {
console.log(`${this.name}吃${food}`)
}
say(sa: string): void {
console.log(`${this.name}说${sa}`)
}
}
const xiao : XiaoLaoHu = new XiaoLaoHu('老虎')
xiao.eat('肉')
xiao.say('你好')
public:公共修饰符
注意: 表示属性或方法都是公有的,在类的内部,子类的内部,类的实例都能被访问,默认情况下,为public
class People {
public name : string
constructor (name:string) { //构造函数必须写
this.name = name
}
public say () :void {
console.log('你好')
}
}
private 私有修饰符
注意: 表示在当前类中可以访问,子类,外部类不可以访问
class People {
private name : string
constructor (name:string) { //构造函数必须写
this.name = name
}
private say () :void {
console.log('你好')
}
}
protected 保护类型
注意: 表示在当前类中和子类中可以访问,外部类不可以访问
class People {
protected name : string
constructor (name:string) { //构造函数必须写
this.name = name
}
protected say () :void {
console.log('你好')
}
}
注意:TypeScript 只做编译时检查,当你试图在类外部访问被 private 或者 protected 修饰的属性或方法时,TS 会报错,但是它并不能阻止你访问这些属性或方法。
readonly 只读修饰符
注意: 表示某个属性是只读的,不能被修改
class People {
readonly name : string
constructor (name:string) { //构造函数必须写
this.name = name
}
}
class People {
name : string //默认为public
age : number
constructor (name:string, age:number) { //构造函数必须写
this.name = name
this.age = age
}
say () :void {
console.log('你好')
}
}
const HH : People = new People('含含', 21)
console.log(HH.name)
console.log(HH.age)
HH.say()
class Student extends People {
cm : number
constructor (name:string, age:number, cm:number) {
super(name, age) //super 继承父类的构造函数,并向构造函数传参,super必须写在第一行
this.cm = cm
}
work () : void {
console.log('学习')
}
}
const stu1 : Student = new Student('liu', 22, 175)
console.log(stu1.name)
console.log(stu1.age)
console.log(stu1.cm)
stu1.say()
stu1.work()
注意: 类可以实现(implement)接口。通过接口,你可以强制地指明类遵守某个契约。你可以在接口中声明一个方法,然后要求类去具体实现它。
接口不可以被实例化,实现接口必须重写接口中的抽象方法
interface Play {
plays (difang:string) : void;
}
class Playy implements Play {
plays(difang: string): void {
console.log(`我们要去${difang}玩!!!`)
}
}
const pl : Playy = new Playy();
pl.plays('北京')
类可以实现(implement)多个接口,但只能扩展(extends)自一个抽象类。
抽象类中可以包含具体实现,接口不能。
抽象类在运行时是可见的,可以通过 instanceof判断。接口则只在编译时起作用。
接口只能描述类的公共(public)部分,不会检查私有成员,而抽象类没有这样的限制。
TypeScript 对比 JavaScript 来讲是一门强类型语言吗,不能更改原有的类型
例如在 JavaScript 中
let a = '1';
a = 1;
console.log(a); // 10
在上面的代码中,我们可以看到 a 刚开始是 String 类型,后面赋值 10 变成了 Number 类型
在 TypeScript 中
let a = '1';
a = 1; // Type '1' is not assignable to type 'string'
因为强类型语言是一种强制类型定义的语言,所以一开始 a的值 string 后面就不能赋上其它类型的值
在 TypeScript 也可以这样子来定义变量
let a: string = '1';
这个叫做 typeScript 的原型,其他原型
let a: string = '1';
let num: number = 1;
let ifLogin: boolean = false;
// 一种特殊的类型
let anything;
anything = 1;
anything = '1';
可以不对当前值做初始化,此时变量可以被赋值为任意值。等同于:
let anything: any;
在 TypeScript 中数组的原型写法
let name: Array<string> = ['a','b']
前面的 Array 是 name 这个变量的值的类型,<>里面是数组里面的值的类型。
let name: Array<string> = ['a','b']
console.log(name[0]);
转换成 js 在浏览器打印出 console.log(name[0]) 打印出 a。
let name: Array<string> = ['a','b']
name[0] = 100;
报错 Type ‘100’ is not assignable to type ‘string’
let name: Array<string> = ['a','b']
name[0] = '100';
name = 'a'; // 报错
name = ['100']; // 没问题
name = [100] // 报错
因为在初始化变量 name 的时候就定义好了是 array 类型,数组里面值的类型是 string。
多种初始化数组变量,定义数组变量也可以这样写:
let numbers: Array<number> = [1, 2, 3];
let numbers: number[] = [1, 2, 3];
let anyArray: any[] = [1, '2', false];
可以在数组里面设置多个类型值
let box: [string, number] = ['hello', 2];
注意:
元组和数组看起来有点类似,但是,是有区别的
元组的长度是有限的,而且分别为每一个元素定义了类型
enum—>组织收集一组相关变量的方式。
字符串枚举
enum Color{
Black,
Yellow,
Red
}
let myColor: Color = Color.Red;
console.log(myColor); // 输出 3
枚举是存的数值,而不是打印出属性 Red
注意:
数字的枚举---->下标从0开始,也可以自行设置枚举成员的初始值,它们会依次递增
字符串枚举
enum SIJI {
chun = '春',
xia = '夏',
qiu = '秋',
dong = '冬'
}
console.log(SIJI.chun)//春
console.log(SIJI.xia)//夏
console.log(SIJI.qiu)//秋
console.log(SIJI.dong)//冬
注意:
字符串枚举类型允许使用字符串来初始化枚举成员,可以是一个字符串字面量或者另一个字符串的枚举成员
let num1 : number = 20;
let num2 : number = 175.5;
let a1 : number = Infinity; //正无穷大
let a2 : number = -Infinity; //负无穷小
let a3 : number = NaN;
注意: Infinity, -Infinity, NaN 也属于Number类型
let un : undefined = undefined;
注意:
undefined 类型的数据只能被赋值为 undefined
在 typescript中,已声明未初始化的值要直接访问的话,类型需要定义为undefined
let nu : null = null;
注意: null 类型只能被被赋值为null
null是一个空指针对象,undefined是未初始化的变量,所以,可以把undefined看成一个空变量,把unll看成一个空对象。
特别注意: 默认情况下,undefined 和 null 类型,是所有其它类型的子类型,也可以说成,它俩可以给所有其他类型赋值。
function returnVal():string{
return 'hahaha';
}
console.log(returnVal())
打印出 hahaha
function returnVal():string{
return 100;
}
console.log(returnVal())
打印报错
function box(val1,val2){
return val1+val2
}
box(1, '2'); // 输出 '12';
// 等同于
function box(val1:any, val2:any){
return val1 + val2
}
box(1, '2'); // 输出 '12';
// 可以定义类型
function box(val1:number, val2:number){
return val1 + val2
}
box(1, 2); // 输出 3
box(1, '2'); //报错
function box(val1:number, val2:number):number{
return val1 * val2
}
box(1, 2); // 输出 3
//值类型
let str : string = '你好!'
//引用类型
let str1 : String = new String('你好!')
let boo : boolean = true;
let boo1 : boolean = false
let sy : symbol = Symbol('bar');
注意: symbol类型的值是通过Symbol构造函数创建的。
function say():viod {
console.log('hahaha');
}
function box(val1:number, val2:number){
return val1 + val2
}
let myfunc;
myfunc = say;
myfunc(); // 输出 hahaha
myfunc = box;
myfunc(5, 5); // 输出 10
这里的 myfunc 是 viod 类型,可以存储不同的函数
function say():viod {
console.log('hahaha');
}
function box(val1:number, val2:number){
return val1 + val2
}
let myfunc: (a: number,b:number) => number; // 给函数、返回值指定类型
myfunc = say; // 报错
myfunc(); // 报错
myfunc = box;
myfunc(5, 5); // 输出 10
let obj = {
name: "cheng",
age: 20
};
obj = {} // 报错
// 因为初始化的时候已经给 obj 这个对象设置了属性和属性值类型
obj = {
a: 'cheng',
b: 20
} // 报错
// 因为初始化的时候已经给 obj 设置格式, 包含了 name,age 所以在 obj 里面要有 name 和 age
完整写法
let obj:{name: string, age: number} = {
name: "cheng",
age: 20
};
obj = {
name: 'wu',
age: 18
}
修改正确
let comp: {data: number[], myfunc:(itme: number)=> number[]} = {
data: [1, 2, 3],
myfunc: function(itme: number):number[]{
this.data.push(itme);
return this.data;
}
};
console.log(comp.myfunc(20)); // 输出 [1, 2, 3, 20]
type IType = {data: number[], myfunc:(itme: number)=> number[]};
let comp: IType = {
data: [1, 2, 3],
myfunc: function(itme: number):number[]{
this.data.push(itme);
return this.data;
}
};
console.log(comp.myfunc(10)); // 输出 [1, 2, 3, 10]
let unionType:any = 12;
unionType = '12';
let a:number| string| boolean = 12;
a = '12';
a = true;
a = {}; // 报错
let checkType = 10;
if(typeof checkType == "number"){
console.log('number');
}
let a = null;
a = undefined;
let myNull = 12;
// 可以在非严格模式下设置,不报错
myNull = null;
never 类型是任何类型的值类型,也可以赋值给任何类型。然而没有类型是 never 的子类型或可以赋值给 never 类型(除了 never 本身之外)。any 也不可以赋值给 never。通常表现为抛出异常或无法执行到终止点(例如无线循环)。
let x:never;
x = 12; // 报错: 不能将其他类型转为 never 类型
let y: number;
y = (()=>{
throw new Error(msg)
})();
可以正常执行
function error (msg: string):never {
throw new Error(msg)
}
function loop():never{
while (true){}
}
今天学习了