先装上typescript包,新增脚本
npm i typescript 装ts包
tsc --init 生成tsconfig.js配置文件
@types开头的包 都是ts声明文件,可以进入node_modules/@types/xx/index.d.ts进行查看
ts-loader 可以让wwebpack使用ts的标准配置文件tsconfig.json编译ts代码
"scripts": { "dev":"tsc index.ts --watch" }
npm run dev 执行命令,将index.ts文件 翻译为index.js文件
ts声明方式
布尔值
let flag:boolean = true;
数字
let count:number = 1;
let hexLiteral: number = 0xf00d;//十六进制数字
let binaryLiteral: number = 0b1010; //二进制数字
let octalLiteral: number = 0o744;//八进制数字
字符串
let name:string = 'zanlan';
数组 方式一
//直接使用[ ] 前面加上内部的类型标识
let list: number[] = [1, 2, 3];
数组 方式二
// 数组泛型 Array<元素类型>
let list: Array<number> = [1, 2, 3];
元组 Tuple
//元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let x: [string, number] = ['hello', 10];
**类型一 | 类型二 | 类型三 .... **
//多种类型并集 只需要满足一种就行
let age:number | undefined | null = 10
枚举
//默认序号 从0开始
enum Types { type1, type2, type3 }
let g1:Types = Types.type1 // => 0
let g2:Types = Types.type2 // => 1
let g3:Types = Types.type3 // => 2
console.log(Types)
{
'0': 'type1', '1': 'type2', '2': 'type3',
type1: 0, type2: 1, type3: 2
}
//自定义序号 从1开始 后面序号自动递增
enum Types { type1 = 1, type2, type3 }
//完全自定义序号
enum Types { type1 = 1, type2 = 3, type3 = 5 }
any
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
let list: any[] = [1, true, "free"];
list[1] = 100;
void
//标识函数没有返回值 及时有也只能是返回null或者undefined
function greeting(name: string):void {}
//声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null:
let unusable: void = undefined;
Null 和 Undefined
//设置的值时固定的 所以没有太大用处
let u: undefined = undefined;
let n: null = null;
//默认情况下null和undefined是所有类型的子类型。 可以把 null和undefined赋值给number类型的变量。
let num1:number = undefined;
let num2:number = null;
// 指定strictNullChecks为true时,null和undefined只能赋值给void类型变量
//这种严格情况下,如果确实想给某个变量允许给其设置null和undefined,可以这样,如下
let num3:string | null | undefined = undefined;
never
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
类型断言
let znames: number | string | null ;
function ww(){
console.log((znames as string).length)
}
znames = "13123"
ww()
let someValue: any = "this is a string";
let strLength1: number = (<string>someValue).length;//和下面的效果一样
let strLength2: number = (someValue as string).length;
不可以这样写
let a = 1
let b = 'b'
let c: a | b = 1
可以这样写
let c: 1 | 'b' = 1
可以这样写
type cType = 1 | 'b'
let c:cType = 1
函数类型声明
type GetFunction = (x:string,y:string)=>string;
let getUsername:GetFunction = function(f:string,l:string):string{
return f+l
}
函数形参声明
function print(name:string,age?:number):void{} // age参数可以不传
function print(name:string,age:number=24):void{} // age默认值为24
函数形参聚合为数组
function sum(...number:Array<number>){
console.log(number)
}
sum(1,2,3,4)
函数一个形参多种类型
function attr(val:string|number){
if(typeof val === 'string'){
console.log(val,'string')
}else{
console.log(val,'number')
}
}
函数一个形参多种类型
function attr(val:string):void;
function attr(val:number):void;
function attr(val:any){
if(typeof val === 'string'){
console.log(val,'string')
}else{
console.log(val,'any')
}
}
attr('asdfad')
attr(123)
声明实例属性myname
class Person {
myname:string; // strictPropertyInitialization 设置为false,否则报错
}
声明实例属性myname
class Person {
myname:string = 'zanlan'; 不会报错
}
声明实例属性myname
class Person {
myname:string'; //不会报错
constructor(x:string){
this.myname = x
}
}
访问控制修饰符 public protected private
- public 都可以访问到,没有限制
class Animal{
public readonly name:string = 'x'
constructor(name:string){
this.name = name
}
}
class Dog extends Animal{
getName(){
console.log(this.name)//能访问
}
}
let a = new Animal('zanlan')
console.log(a.name)//能访问
- protected 只能是在当前的class内部使用,和他的子类的内部使用
class Animal{
protected readonly name:string = 'x'
constructor(name:string){
this.name = name
}
getName(){
console.log(this.name)//能访问 输出 "zanlan"
}
}
class Dog extends Animal{
getName(){
console.log(this.name)//能访问 输出 "zanlan"
}
}
new Animal('zanlan').getName()//能访问
new Dog('zanlan').getName()//能访问
new Animal('zanlan').name // 报错
new Dog('zanlan').name // 报错
//报错信息为
//Property 'name' is protected and only accessible within class 'Animal' and its subclasses.
- private 只能是在当前的class内部使用,和他的子类的内部使用
class Animal{
private readonly name:string = 'x'
constructor(name:string){
this.name = name
}
getName(){
console.log(this.name)//能访问 输出 "zanlan"
}
}
class Dog extends Animal{
getName(){
console.log(this.name)// 报错
}
}
new Animal('zanlan').getName()//能访问
new Animal('zanlan').name // 报错
/* 报错信息 为 */
Property 'name' is private and only accessible within class 'Animal'.
重写(override)--子类重写继承自父类中的方法
class Animal {
speak(word:string):string{
return '叫' + word
}
}
class Cat {
speak(word:string):string{
return '猫叫' + word
}
}
let cat = new Cat()
console.log(cat.speak('喵喵喵'))
重载(overload)-- 同一个函数提供多个类型定义
function double(val:number):number
function double(val:string):string
function double(val:any):any{
if(typeof val === 'number'){
return val*2
}
return val+val
}
接口interface
只要满足接口的必要条件就可以 但是需要用变量赋值 否则必须是充要条件
interface LabelledValue{
label:string
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);// 不报错
//对象字面量会被特殊对待而且会经过 额外属性检查,当将它们赋值给变量或作为参数传递的时候。
//如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。
printLabel({size: 10, label: "Size 10 Object"});//报错
? 代表是 可以不传的意思
interface Speakable{
name?:string;
}
let speakman:Speakable = {}
可索引的类型 表示数组
interface UserInterface {
[index: number]: string
}
let arr: UserInterface = ['zf', 'jg']
console.log(arr) // ['zf', 'jg']
可索引的类型 表示对象
interface UserInterface2 {
name:string;
[index: string]: string;
}
let obj2: UserInterface2 = { name: 'zhufeng' }
console.log(obj2)// { name: 'zhufeng' }
接口相互继承用extends class类实现接口特征
interface AnimalLike{
eat():void;
}
interface PersonLike extends AnimalLike{
speak():void;
}
// 如果继承多个接口 则用逗号隔开连续写 implements Flying,Eating
class Girl implements PersonLike{ //implements 工具,实现
eat(){}
speak(){}
}
class类作为参数传递 接口可以规定class类应该有哪些属性
interface WithNameClass{
new(name:string):Animal // 在new Animal时,构造函数要传个字符串
age:number // Animal类的静态属性age 为number 也可以是 Animal.prototype上上的属性
getAge():void// Animal类的静态属性getAge为number 也可以是 Animal.prototype上上的属性
}
class Animal {
constructor(public name:string){}
static age:number = 1; // 声明Animal.age
getAge(){ // 声明Animal.prototype.getAge
console.log(Animal.age)
}
}
function creaetClass(clazz:WithNameClass,name:string){
//书写clazz会报错 解决办法是 "noImplicitAny": false
return new clazz(name)
}
console.log(creaetClass(Animal,'123'))//Animal { name: 'zanlan' }
使用implements,接口只能修饰class类的静态属性 以及类的prototype属性,不可以修饰实例属性
interface ClockConstructor {
new (hour: number, minute: number);
}
class Clock implements ClockConstructor {//无法修饰constructor内数属性,所以报错
constructor(h: number, m: number) { }
}
//下面是解决办法 只有当被new时候,才会修饰到constructor函数
interface ClockConstructor {
new (hour: number, minute: number):Clock;
}
class Clock{//无法修饰constructor内数属性,所以报错
constructor(h: number, m: number) { }
}
对多个class类的constructor 和属性做统一修饰
interface A {// A为构造函数所用
new(a: string, b: number): B;
}
interface B {//B为实例方法所用。
tick();
}
class F1 implements B {
constructor(a: string, b: number) { }
tick() { }
}
class F2 implements B {
constructor(a: string, b: number) { }
tick() { }
}
function createF(F: A, a: string, b: number):B {
return new F(a, b)
}
createF(F1, '1', 1)
createF(F2, '2', 2)
接口继承类
类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
//接口继承了一个类的类型时,它会继承类的成员 但不包括其实现
class A {
private state: any;
}
interface F extends A { // F接口
select(): void;
}
class C implements F {
state = 1 // 报错 “state”在类型“F”中是私有属性
select() { }
}
只读属性 readonly ReadonlyArray
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
let ro: ReadonlyArray<any> = ['1', true, 1];
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
接口修饰 普通函数 规定传参 以及返回值
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(source: string, subString: string) {
return true;
}
修饰符
public 在TypeScript里,成员都默认为 public。 private 被修饰属性,只能在当前类里面使用,其他地方都不可以用,包括实例 protected 被修饰属性,只能在当前类,以及子类里面使用,其他地方都不可以用,包括实例 protected constructor
class F {
protected constructor() { }
}
new F(); // 错误: 'F' 的构造函数是被保护的,只能通过继承才能访问F的构造函数
两个不相干的类 内部有private或者protected属性,则它们类的实例不可以相互做赋值操作
class A {
private name: string;
constructor(theName: string) { this.name = theName; }
}
class B extends A {
constructor() { super("B"); }
}
class C {
private name: string;
constructor(theName: string) { this.name = theName; }
}
let a = new A("aaaaa");
let b = new B();
let c = new C("ccccc");
a = b;
a = c; // 错误: a 与 c 不兼容.
readonly 只读属性必须在声明时或构造函数里被初始化。
class Octopus {
readonly name: string;
readonly age: number = 8;
constructor (v: string) {
this.name = v;
}
}
存取器
TypeScript支持通过getters/setters来截取对对象成员的访问 只带有 get不带有 set的存取器自动被推断为 readonly
class Person {
myname:string = 'zanlan'; //声明this.myname = "zanlan"
get name() { //声明 Person.prototype.name
return this.myname.toUpperCase()
}
set name(newName: string) {
this.myname = newName
}
}
一般情况下回报错
Accessors are only available when targeting ECMAScript 5 and higher
解决办法是 指定编译版本为es5的
tsc index.ts --watch -t es5
抽象类
抽象类只能被继承,且其内部申明的抽象方法,必须在子类中实现
abstract class A {
f1(): void {}
abstract f2(): void; // 必须在派生类中实现
}
class B extends A {
f2(): void {}
f3(): void {}
}
let a: A; // 允许创建一个对抽象类型的引用
a = new A(); // 错误: 不能创建一个抽象类的实例
a = new B(); // 允许对一个抽象子类进行实例化和赋值
a.f1();
a.f2();
a.f3(); // 错误: 方法在声明的抽象类中不存在
typeof class 取class类的类型
typeof A,意思是取A类的类型,而不是实例的类型。 或者更确切的说,"告诉我 A标识符的类型",也就是构造函数的类型。 这个类型包含了类的所有静态成员和构造函数
class A {}
let B: typeof A = A;
TypeScript 函数传参
不可多传 不可少传,除非是?号参数
function F(a: string, a: string) {
return a+ " " + b;
}
F("a"); // error, too few parameters
F("a", "b", "c"); // error, too many parameters
F("a", "b"); // ah, just right
参数合并
function F(a: string, ...arr: string[]) {
return a + " " + arr.join(" ");
}
F("a", "b", "c", "d");
泛型
T变量表示类型,捕获用户传入的类型 方式一 需要用户手动传类型
function F<T>(length: number, value: T): T[] {}
F<string>(3, 'x')
方式二 系统自动识别类型 这种更加普遍
F(3, 'x')
如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的情况下,这是可能出现的。
泛型 在 接口里应用
interface InterF<T> {
(arg: T): T;
}
function F<T>(arg: T): T {
return arg;
}
let myIdentity: InterF<number> = F;
多个泛型
function F<A, B>(arr: [A, B]): [B, A] {
return [arr[1], arr[0]]
}
F([1, 'a']) //['a',1]
泛型继承接口 接口描述泛型的属性类型
interface Len{
length:number
}
function logger<T extends Len >(val:T){
console.log(val.length)
}
logger('xxx-')
接口使用泛型
interface Cart<T>{
list:T[]
}
let cart:Cart<number> = {list:[1,2,3]}
泛型别名
type Cart<T> = { list: T[] } | T[]
let c1: Cart<number> = { list: [1, 2, 3] }
let c2: Cart<number> = [1, 2, 3]
类数组
let root = document.getElementById('root')
let children:HTMLCollection = root!.children;
let childNodes:NodeListOf<ChildNode> = root!.childNodes
strictNullChecks为true,则代码中不能调用可能为null的变量的属性了
function getFirstLetter(str:string | null){
console.log(typeof str.trim())//str可能为null,所以会报错
}
getFirstLetter(null)
?. 是否为undefined/null,是则返回undefined
a?.b;
a == null ? undefined : a.b;
let a:A['job'] = { jobname:'程序员' }
type C = A | B;
索引类型查询操作符
interface Person {
name: string;
age: number;
gender: 'male' | 'female'
}
type PersonKey = keyof Person;//索引类型查询
function getValueByKey(p: Person, key: PersonKey) {
return p[key]
}
let val = getValueByKey({ name: 'zhufeng', age: 10, gender: 'male' }, 'name')
console.log(val)
in 结合keyof,不加问号会报错
类型“{ name: string; }”缺少类型“Part”中的以下属性: age, gender
in 结合keyof,加了问号后,是允许不传name,age,gender属性
interface Person{
name:string;
age:number;
gender:'male'|'female'
}
type Part<T> = {
[key in keyof T]?:T[key]
}
let p:Part<Person> = {name:'zanlan'}
console.log(p)// { name: 'zanlan' }
Partial 可以将传入的属性由可选变为可选,具体使用如下
interface A {
a1: string;
a2: number;
a3: boolean;
}
type aPart = Partial<A>;
const a: aPart = {a1:'zanlan'};
console.log(a)//{a1:'zanlan'}
内置的Partial方法实现
type Partial<T> = {
[p in keyof T]?: T[p];
}
Required可以将传入的属性中的可选项变成必选项,这里用了-?修饰符
interface Person{
name:string;
age:number;
gender?:'male' | 'famale'
}
let p1:Person = {
name:'zhufeng', // 没有加Required 这里不会报错
age:10
}
let p2:Required<Person> = {
name:'zhufeng',
age:10,
gender:'male' // 必须要写gender 否则报错
}
内置的Required方法实现
type Required<T> = {
[p in keyof T]-?: T[p];
}
Readonly通过为传入的属性每一项都加上Readonly修饰符来实现。
内置的Readonly方法实现
type Readonly<T> = {readonly [p in keyof T]:T[p]}
Pick
interface Animal{
name:string;
age:number;
getName():void;
}
type AnimalSub = Pick<Animal,'name'|'age'>;
let a:AnimalSub = {
name:'zhufeng',
age:10
}
实现方法
type Pick<T, K extends keyof T> = {[P in K]: T[P]};
Exclude 除了某某之外
type A = Exclude<string|number|boolean,string>;
let a:A = 10;
let b:A = true;
let c:A = 'xx';//报错
interface Face1{};
interface Face2{};
type C = Exclude<Face1|Face2,Face1>;
Extract 提取出某某有效
type A = Extract<string | number | boolean,string>;
let a:A = 'hello';
let b:A = 1;//报错
let c:A = true;//报错
NonNullable 剔除null 和 undefined类型
type D =NonNullable<string | number | null | undefined>;
let e:D = null;//不能将类型“null”分配给类型“D”
let f:D = undefined;//不能将类型“undefined”分配给类型“D”
ReturnType获取函数类型的返回类型
function getUserInfo(){
return {name:"zhufeng",age:10}
}
type UserInfo = ReturnType<typeof getUserInfo>;
const userA:UserInfo = {
name:'zhufeng',
age:true,//报错
ww:'fsadf'//报错
}
局部声明全局属性
declare global{ // 代表在局部内 声明全局的属性
interface String{
double():string;
}
interface Window {
myname:string;
}
}
String.prototype.double = function(){
return this+this
}
console.log('hello'.double())
window.myname = '123'
console.log(window.myname)
export {} // 加了这个 代表是上面的声明是局部的
class和interface区别
class Person {
name:stirng = 'hello'
}
// Person 既可以作为类使用 也可以做值来使用
let p1 = Person;
let p2 = new Person()
//接口只能作为类使用,不可以作为值赋值给变量
interface Person1 {
name:string;
}
let p3:Person1;
let p4 = Person1; //报错 “Person1”仅表示类型,但在此处却作为值使用
合并声明
interface Person {//一个文件声明了Person
name: string;
}
interface Person {//另一个文件也声明了Person
age: number;
}
let p: Person = { //因为同名 在ts文件使用时候 就会被合并
name: '123',
age: 123
}
namespace扩展类,用于表示内部类
class Form {
username:Form.Item = '';
password:Form.Item = '';
}
namespace Form{
export class Item {}
}
let item:Form.Item = new Form.Item()
console.log(item)
enum Color {
red = 1,
yellow = 2
}
namespace Color {
export const green = 4;
export const purple = 5;
}
console.log(Color.green) // 4
导出接口
export declare type PropType<T> = PropConstructor<T> | PropConstructor<T>[];//vue中导出
export interface ListItem {//自定义的
avatar: string;
title: string;
datetime: string;
type: string;
description: string;
status?: "" | "success" | "warning" | "info" | "danger";
extra?: string;
}
const props = defineProps({
noticeItem: {
type: Object as PropType<ListItem>,
default: () => {title:'123'},//定义default 和 validator 必须用到箭头函数
validator: (book: Book) => !!book.title
}
});
{{ props.noticeItem.title }} //定义好props参数 直接使用
{{ props.noticeItem?.extra }}
定义computed返回类型
computed: {
greeting(): string {// 需要注解
return this.message + '!'
}
}
泛型表示法
const year = ref<string | number | boolean>("2020");
year.value = true;
父组件 调用子组件方法
import { defineComponent, ref } from 'vue'
const MyModal = defineComponent({
setup() {
const isContentShown = ref(false)
const open = () => (isContentShown.value = true)
return {
isContentShown,
open
}
}
})
const app = defineComponent({
components: {
MyModal
},
template: `
<button @click="openModal">Open from parent</button>
<my-modal ref="modal" />
`,
setup() {
const modal = ref()
const openModal = () => {
modal.value.open()
}
return { modal, openModal }
}
})
它可以工作,但是没有关于 MyModal 及其可用方法的类型信息。为了解决这个问题,你应该在创建引用时使用 InstanceType:
setup() {
const modal = ref<InstanceType<typeof MyModal>>()
const openModal = () => {
modal.value?.open()
}
return { modal, openModal }
}
类型声明 reactive
import { defineComponent, reactive } from 'vue'
interface Book {
title: string
year?: number
}
export default defineComponent({
name: 'HelloWorld',
setup() {
const book = reactive<Book>({ title: 'Vue 3 Guide' })
// or
const book: Book = reactive({ title: 'Vue 3 Guide' })
// or
const book = reactive({ title: 'Vue 3 Guide' }) as Book
}
})
类型声明 computed
import { defineComponent, ref, computed } from 'vue'
export default defineComponent({
name: 'CounterButton',
setup() {
let count = ref(0)
// 只读
const doubleCount = computed(() => count.value * 2)
const result = doubleCount.value.split('')
// => Property 'split' does not exist on type 'number'
}
})
事件绑定e.target需要声明类型
<template>
<input type="text" @change="handleChange" />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
setup() {
// `evt` 将会是 `any` 类型
const handleChange = evt => {
console.log(evt.target.value) // 此处 TS 将抛出异常
}
return { handleChange }
}
})
</script>
如你所见,在没有为 evt 参数正确地声明类型的情况下,当我们尝试获取 元素的值时,TypeScript 将抛出异常。解决方案是将事件的目标转换为正确的类型:
const handleChange = (evt: Event) => {
console.log((evt.target as HTMLInputElement).value)
}