1、静态类型语言 和 动态类型语言
-
静态类型语言
在编译阶段确定所有变量的类型
在编译阶段确定属性偏移量
用偏移量访问代替属性名访问
偏移量信息共存 -
动态类型语言
在执行阶段确定所有变量的类型
在程序运行时,动态计算属性偏移量
需要额外的空间存储属性名
所有对象的偏移量信息各存一份
静态类型语言 | 动态类型语言 |
---|---|
对类型嫉妒严格 | 对类型非常宽松 |
立即发现错误 | Bug 可能隐藏数月甚至数年 |
运行时性能好 | 运行时性能差 |
自文档化 | 可读性差 |
动态类型语言的支持者认为:
- 性能是可以改善的,而语言的灵活性更重要(V8引擎)
- 隐藏的错误可以通过单元测试发现
- 文档可以通过工具生成
强类型、动态语言:python
弱类型、动态语言:javascript、PHP
强类型、静态语言:Java、c#、Go、C、C++
2、数据类型
ES6的数据类型: undefined、null、Boolean、Number、String、Bigint、Array、Function、Object、Symbol
TS 的数据类型:undefined、null、Boolean、Number、String、Bigint、Array、Function、Object、Symbol、void、any、never、元祖、枚举、高级类型
类型注解作用:相当于强类型语言中的类型声明
语法:(变量/函数):type
1、基础类型
// 元祖
let tuple: [number, string] = [1, '2'];
// 对象,和es6中的object不一样
let obj: object = {x: 3, y: 4};
// symbol
let s1: symbol = Symbol();
// void
let noFun = void 0;
// undefined并不是js中的关键字,所以容易被重置掉,所以建议使用void 0;去生成undefined;
(function(){
var undefined = 0
})
// never 永远不会返回
let error = () => {
throw new Error('xxxx');
}
let endless = () => {
while(true){}
}
2、枚举类型
枚举:一组具有名字的常量集合。
枚举类型:数字枚举、字符串枚举、异构枚举、常量枚举
// 数字枚举
enum Role {
Weman,
Man,
}
// 字符串枚举
enum Role {
Success='1',
Fail='0',
}
// 异构枚举,容易混淆,不建议使用
enum Answer {
No,
YES = 'Yes'
}
// 常量枚举
const enum Month {
Jan,
Feb,
Mar,
}
3、接口
接口定义对象
interface List {
readonly id: number;
name: string;
// 字符串索引签名
// [key: string]: any;
// 数字索引签名
// [index: number]: string;
age?: number;
}
interface Names {
[x: string]: string;
// [y: number]: number;
[y: number]: string;
}
// 断言 两种写法
{id: 1, name: 'lili', c:'ssss'} as List
<List>{id: 1, name: 'lili', c:'ssss'}
接口定义函数的方式
// 方式1
function add1(x: number, y: number)=> {
return x +y;
}
// 方式2
let add2: (x: number, y: number)=> number;
add2 = (x, y)=> x+y;
// 方式3
interface Add {
(x: number, y: number):number
}
// 方式4 类型别名
type Add = (x: number, y: number) => number;
let add: Add = (x, y)=> x+y;
函数定义参数要求
// 可选参数
function add5(x: number, y?: number) {
return y ? x + y: x;
}
// 必选参数之后,参数可以不传
function add6(x: number, y=0, z:number, q=1) {
return x+y+z+q;
}
// 剩余参数
function add7(x: number, ...rest: number[]) {
return x + rest.reduce((pre, cur)=> pre+cur);
}
// 函数重载,把容易匹配的放前面
function add8(...rest: number[]): number;
function add8(...rest: string[]): string;
function add8(...rest: any[]): any {
let first = rest[0];
if (typeof first === 'string') {
return rest.join(' ');
}
if (typeof first === 'number') {
return rest.reduce((pre, cur)=> pre+cur);
}
}
4、类
类成员的属性都是实例属性,而不是原型属性。类成员的方式都是原型方法。
类成员的修饰符:public、private、protected、readonly、static
protected受保护的成员只能在类和子类中访问。static可以被继承
class Dog {
constructor(name: string) {
this.name = name;
}
public name: string;
run(){ }
private fun(){ }
protected pro(){ }
readonly legs: number = 4;
static food: string = 'bones';
}
// 继承
class Husky extends Dog{
constructor(name: string, public color: string){
super(name);
this.color = color;
this.pro();
}
}
抽象类只能被继承,不能被实例化的类
abstract class Animal {
eat(){ }
// 抽象方法,明确子类有其他实现,就没有必要在子类中实现了
abstract sleep(): void;
}
class Dog extends Animal{
constructor(public name: string){
super();
this.name = name;
}
run(){ }
sleep(): void {
console.log('dog');
}
}
// 多态
class Cat extends Animal{
sleep(): void {
console.log('cat')
}
}
链式调用通过返回this实现
// 链式调用
class WorkFlow {
sleep1(){
return this;
}
sleep2(){
return this;
}
}
class MyFlow extends WorkFlow{
next(){
return this;
}
}
let myFlow = new MyFlow();
myFlow.next().sleep1().next().sleep2();
类与接口的关系
// 1、接口之间,是可以相互继承的,实现接口复用
interface Human{
name: string;
eat(): void
}
interface Man extends Human{
run(): void;
}
interface Child{
cry(): void;
}
interface Boy extends Man, Child{ }
// 2、类之间,是可以相互继承的,实现方法和属性复用
class A {
}
class B extends A{
}
// 3、接口可以通过类来实现,接口只能约束类的公有成员
class Asian implements Human{
constructor(public name: string){
this.name = name;
}
eat(){
}
}
// 4、接口可以抽离出类的成员,抽离包括公有、私有、受保护成员
class Auto{
state = 1;
}
interface AutoInterface extends Auto{
}
class Bus implements AutoInterface{
state = 1;
}
5、泛型
泛型的好处:
- 函数和类可以轻松的支持多种类型,增强程序的扩展性
- 不必写多条函数重载,冗余的联合类型声明,增强代码可读性
- 灵活控制类型之间的约束
泛型函数
// 函数重载
function log(value: string): string;
function log(value: string[]): string[];
function log(value: any): any{
return value
}
// 联合类型
function log2(value: string | string[]): string | string[]{
return value;
}
// 泛型
function log3<T>(value: T): T{
return value;
}
泛型接口
// 别名写法
type Log<T=string> = (value: T)=> T;
// 接口写法
interface Log<T= string> {
(value: T): T;
}
function log3<T>(value: T): T{
return value;
}
let mylog: Log= log3;
let mylog2: Log<number>= log3;
mylog('111');
mylog2(222);
泛型类
class Log<T> {
run(value: T){
return value;
}
}
let log4 = new Log<number>();
log4.run(111);
泛型继承
interface Length{
length: number;
}
function log6<T extends Length>(value: T): T{
return value;
}
log6('xxxxx');
log6(['xxxxx']);
3、类型检测机制
类型检测机制指TpyeScript 编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为。它能辅助开发,提高开发效率。
- 类型推断
- 类型兼容性
- 类型保护:TypeScript 能够在特定的区块中保证变量属于某种确定的类型。可以在此区块中放心的引用此类型的属性,或者调用此类型的方法。
enum LangType {
Strong,
Week,
}
class Java {
java: any;
helloJava() {
console.log('java');
}
}
class JavaScript {
javaScript: any;
helloJavaScript() {
console.log('JavaScript');
}
}
// 类型谓词 is
function isJava(lang: JavaScript | Java): lang is Java{
return (lang as Java).helloJava !== undefined;
}
function getLanguage(type: LangType, x: string | number){
let lang = type === LangType.Strong ? new Java() : new JavaScript();
if((lang as Java).helloJava) {
(lang as Java).helloJava();
} else {
(lang as JavaScript).helloJavaScript();
}
// 类型保护函数
if(isJava(lang)) {
lang.helloJava()
} else {
lang.helloJavaScript();
}
// instanceof
if(lang instanceof Java) {
lang.helloJava();
} else {
lang.helloJavaScript();
}
// in
if('java' in lang) {
lang.helloJava();
} else {
lang.helloJavaScript();
}
// typeof
if(typeof x === 'string') {
x.length
} else {
x.toFixed();
}
return lang;
}
getLanguage(LangType.Strong, 11111);
4、高级类型
交叉类型 比较适合做对象的混入
interface DogInterface {
run(): void;
}
interface CatInterface {
jump(): void;
}
let pet: DogInterface & CatInterface = {
run(){},
jump(){},
}
联合类型 可以使类型具有一定的不确定性
// 可区分的联合类型
interface Square {
kind: 'square';
size: number;
}
interface Rectangle {
kind: 'rectangle',
width: number;
height: number;
}
interface Circle {
kind: 'circle',
r: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape): number {
switch(s.kind) {
case 'square':
return s.size * s.size;
case 'rectangle':
return s.height * s.width;
case 'circle':
return 2 * Math.PI * s.r;
default:
// 检测s是不是never类型
return ((e: never)=> {throw new Error(e)})(s)
}
}
area({kind: 'circle', r: 2222})
索引类型
// keyof T
interface ObjInterface {
a: number;
b: number;
}
let key: keyof ObjInterface = 'b';
// T[K]
let value: ObjInterface['a'] = 22222;
// T extends U
// 应用实例
let obj2 = {
a: 1,
b: 2,
c: 3,
};
function getValue1(obj: any, keys: string[]){
return keys.map((key => obj[key]));
}
const res1 = getValue2(obj2, ['a', 'b']);
// 改造getValue1,约束keys的传参
function getValue2<T, K extends keyof T>(obj: T, keys: K[]): T[K][]{
return keys.map((key => obj[key]));
}
const res2 = getValue2(obj2, ['a', 'b']);
映射类型
映射类型是预先定义的泛型接口,通常还会结合索引类型,获取对象的属性
interface ObjInterface2 {
a: number;
b: number;
c: number;
}
type ReadObj = Readonly<ObjInterface2>;
type PartialObj = Partial<ObjInterface2>;
type PickObj = Pick<ObjInterface2, 'a' | 'b'>;
type RecordObj = Record<'x'|'y', ObjInterface2>;
// 例如Pick的定义
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
条件类型
// T extends U ? X : y
type TypeName<T> =
T extends string ? 'string' :
T extends number ? 'number' :
T extends boolean ? 'boolean' :
T extends undefined ? 'undefined' :
T extends Function ? 'function' :
'object';
type Diff<T, U> = T extends U ? never : T;
type T1 = TypeName<string>;
type T2 = TypeName<string[]>;
type T3 = TypeName<string | string[]>;
// 内置的条件类型
type T4 = Exclude<'a'|'b'|'c', 'a'>;
type T5 = Extract<'a'|'b'|'c', 'a'>;
type T6 = NonNullable<'a'|'b'|'c'| undefined | null>;
网友评论