50个最新TypeScript面试题合集 – TypeScript开发教程-srcmini
ts类型总结: TypeScript高级类型入门手册:附大量代码实例(收藏!)
一、环境安装与运行:
node环境+全局安装npm -g install typescript
新建文件夹→新建test.ts→写代码
运行(先将ts文件转为js文件):
tsc test.ts 自动生成 test.js 再执行 node test.js 即可
或者安装 cnpm install -g ts-node
ts-node first.ts
方式二:
新建立一个项目TSTest,在桌面新建立一个文件夹,然后在VSCode中打开。
打开终端,输入npm init -y,创建package.json文件
在终端中输入tsc --init,创建tsconfig.json文件
安装 cnpm install -g ts-node
修改tsconfig.json配置rootDir和outDir.
新建src文件夹,在里边建立index.html,page.ts文件
编写index.html文件,并引入page.ts文件
编写page.ts文件。
ts-node page.ts
类型汇总
string、number、boolean、object、array
any、unknown、void、never,bigint,symbol
字面量、元祖、枚举、
非严格模式下,undefind和null可以赋值给类型为string或number的变量
never:不存在的值(null/undefined都不符合)定义函数返回值较多,且这个函数是一个抛出错误的函数。
例: function test(msg:string):never{ throw new Error(msg) } //总是抛出错误
object: 只能用于json对象或数组或Date()
例子:let obj: object; obj={'xx':123}或obj=[123]
unkown:未知的类型,不可以赋值给任何类型,除非类型断言后
对象类型:{age?:number,[propname:string]:any} //表示age属性可有可无,且可有额外属性
函数类型: let fn:(a:string,bstring)=>string
数组类型:let a:number[] 或 let a:Array<number> 或 (string|number)[]
元祖类型: let a:[string,number] //用于固定数组的长度和内部元素的类型
if (typeof a === 'string'){}
if(a as string){} // <string>a 说明a的类型断言为string
bigint
表示非常大的数字
let tnum:bigint = 100n
symbol
表示全局唯一
如
let test1 = symbol("name")
let test2 = symbol("name")
test1===test2 //结果是false
声明变量时,要标明变量的类型
//静态类型(基础静态类型),一旦定义数据类型就不可以改变
let value:string="hello word"
//自定义静态类型:
interface mytype {
name:string,
age:number
}
let myvalue : mytype ={
name:"梨花",
age:18
}
console.log(myvalue.name)
对象类型
对象:
let textvalue:{
name:string,
age:number
} = {
name:"小花",
age:18
}
数组
let arr:string[] = ['小红','小花'] //定义一个数组,内容必须是字符串
const arr2:(string|number)[] = ['dsafdsa',2200]
const arr3:{name:string,age:number}[] = [{name:'sdfafas',age:12}]
类
let Person {}
let myclass:Person = new Person()
函数(定义一个函数,返回类型必须是字符串):
let myfun:()=>strung = ()=>{return '小红'}
类型注解、类型推断
类型注解:手动标明变量的类型
类型推断:不需要标明变量的类型,ts能自动推断(定义json时的属性可以直接赋值,不需要标明类型)
类型断言:
方式一:<类型>变量名 如:<string>str
判断str是否为字符串,且打印字符串长度
(<string>str).length
方式二:值 as 类型
return (str as string).length //判断变量str是否为string类型,如果是则打印length,否则报错
//用法:比如函数接收一个参数,但不确定这个参数是字符串还是number,可以用类型断言来写对应的代码
function lxdy(params:(string|number)){
let len = (<string>params).length //先断言为字符串才有length属性
console.log('len',len);
}
函数定义返回值的类型
function func(one:string,two:string):string{
return one+two+1
}
let funcvalue = func('1','2')
如果没有返回值,类型是void
元组
即规定数组每一项的数据类型
let yarr:[string,number,string] = ['d',1,'f']
类型别名,自面量
类型别名(即自定义类型,用type定义):有一些类型很长,为类型另起一个名字
type direction = 'up' | 'down' | 'left' | 'right'
let useValeu:direction = 'up' //只能是上面的四个值
//定义对象类型
type objtype = {
name:string,
age:number
}
//使用:
let myobj:objtype = { //必须要有对应的属性和对应类型的值
name:'xxx'
age:18
}
//定义函数类型:
type mfun = (a:string,b;string) => string
let use:mfun = use(a,b)
//定义交叉变量
interface myface{
name:string,
age:number
}
type myzi = myface & {age:number}
let person:myzi = {name:'xioaming',age:18}
type Ipartial = Partial(myface)
let person:Ipartial = {name:'xioaming'} //创建少一个属性可以
type Iomit = Omit(myface,age)
let person:Ipartial = {name:'xioaming'} //忽略某个属性可以
字面量
如let age:18 = 18 //等于其他值会报错
接口
定义:一系列抽象方法的集合
用关键字 interface 来定义
接口名称一般大写字母开头
interface Inter { //名称为Inter的接口
readonly id:number //不能修改id的值
name:string
age:number
other ?:string //加个问号,表示可有可无
[proname:string] :any //(索引签名)key名称不固定但类型要为string,value 为任何类型
say():string //函数,返回类型是string
}
//约束对象
const inter:Inter ={ //定义这个,要收到接口Inter的约束
name:'dssa',
age:18,
test:19,
say:()=>{
return ''
}
}
//约束类
class myclass implements Inter {
name='dssa';
age=18;
test=19;
say(){
return ''
}
}
//接口的继承
interface Teacher extends Inter {
teach(): string;
}
//使用
function tunc(inter:Inter){
console.log(inter.name)
console.log(inter.age)
console.log(inter.test)
}
接口和类型别名区别
相同点:
1、可约束对象里面有什么属性或方法
2、都可扩展(type使用&,interface使用extends)
不同点:
1、type可以定义为其他基本类型、联合类型、数组等,接口只能定义对象 { }
2、定义同样的名称时候,type不会合并,interface会合并
类
类主要有属性、方法和构造函数
如子类和父类有相同的方法,则子类会覆盖父类的方法
如果子类写了构造函数,则在子类的构造函数中一定要调super()//(即调了父类的构造函数,不然会被覆盖)
class Animal {
age:number;
constructor(age:number){
this.age = age
}
say(){
console.log('汪动物叫')
}
}
class Dog extends Animal {
name:string;
constructor(age:number,name:string){ //构造函数中this指向新创的对象本省
super(age:number)
this.name = name
}
say(){
super.say() //可调用父类的say方法
console.log('汪汪汪')
}
}
const dog1 = new Dog(2,'旺财')
类的访问类型
public:公用内部外部都能使用
protected:内部或者继承的类的内部可以使用
private:只能类的内部,若要修改可再设置类方法修改,外部再调这个方法
static:静态属性(直接用类型.名称访问,实例化的对象访问不了)
class firstcal {
protected name="小明";
public sayMY(){
return this.name;
}
}
类的构造函数
class Person {
public name:string
constructor(name:string){
this.name = name
}
}
//简写
class Person {
constructor(public name:string){
}
}
const person = new Person('4566')
console.log(person.name) //4566
class minPer extends Person {
constructor(public age:number){
super("你好") //子类一定要调用super()
}
}
const minperson = new minPer(18)
console.log(minperson.age) //18
console.log(minperson.name) //你好
类的getter、setter、static
//私有访问域的变量外部不可以访问,则可以通过get、set获取或修改(也叫存取器)
//static 定义的方法或变量不需要new实例化,直接类名就可调用
class Person3 {
constructor(private age:number){
}
get age_(){
return this.age+10
}
set age_(age_:number){
this.age=age_
}
static sayLove() {
return "I Love you";
}
}
let per3 = new Person3(18)
per3.age_ = 15
console.log(per3.age_)
console.log(Person3.sayLove())
抽象类
//抽象类只能被继承,不能实例化
//抽象方法只能在抽象类里,且没有方法体
//继承的类一定要重写这个抽象方法
abstract class cxFj {
abstract skill()
}
class minCX extends cxFj{
skill(){
console.log("技能1")
}
}
const minCX1 = new minCX()
class minCX2 extends cxFj{
skill(){
console.log("技能2")
}
}
const minCX12 = new minCX2()
console.log(minCX1.skill())
console.log(minCX12.skill())
配置文件(tsconfig.json):
运行 tsc --init 生成
运行:tsc 则自动将ts文件转成js文件
修改里面的配置则可以生成对应的js文件
"include":["demo.ts"], //只运行demo.ts
"outDir": "./build", /* 生成js文件的位置 */
"rootDir": "./src", /*ts文件位置*/
"sourceMap": true,/* 生成map文件,部署前可开启,出错会显示转化前代码 */
noUnusedLocals:true //没有使用的变量不转化,并且提示错误
(编译选项详解)
https://www.tslang.cn/docs/handbook/compiler-options.html
联合类型和类型保护(守卫)
//所谓联合类型,可以认为一个变量可能有两种或两种以上的类型
interface Waiter {
anjiao: boolean;
say: () => {};
}
interface Teacher {
anjiao: boolean;
skill: () => {};
}
function judgeWho(animal: Waiter | Teacher) {
//类型断言
if (animal.anjiao) {
(animal as Teacher).skill();
}else{
(animal as Waiter).say();
}
//in语法
if ("skill" in animal) {
animal.skill();
} else {
animal.say();
}
}
//typeof判断哪种类型
function add(first: string | number, second: string | number) {
if (typeof first === "string" || typeof second === "string") {
return `${first}${second}`;
}
return first + second;
}
//用instanceof判断是不是类
class NumberObj {
count: number;
}
function addObj(first: object | NumberObj, second: object | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count;
}
return 0;
}
枚举
定义:用于表示固定的几个取值
例如 人的性别只有男或女
enum Status {
mes,
apa,
dat
}
let val = Status.mes //0
let val = Status[0] //mes
//用法二
enum gtage {
fame=3, //如果不设置默认值,则默认会从0开始赋值,mfame则是1
mfame
}
let enumT:gtage = gtage.fame // 4
//将枚举当作类型来使用
interface Mj {
name:gtage //相当于 name:(gtage.fame|gtage.mfame) 即name:(3|4)
}
class use implements Mj{
name:gtage.fame = 3
}
泛型
泛型,定义的时候不确定类型,调用的时候才来确定
//函数中使用泛型
function fanx2<T>(first:T,second:T){
return `${first}${second}`
}
//类中使用中泛型
interface girsface {
name:string
}
class selectgirs<T extends girsface> {
constructor(private girs:T[]){}
girsname(index:number):string{
return this.girs[index].name
}
}
let myneed = new selectgirs([{name:"dsafsa"},{name:"dsfds5"}]);
console.log(myneed.girsname(1))
//约束泛型(虽然数据类型不确定,但传入的参数一定要含有某个属性,如length)
interface maface {
length:number
}
function echoLength<T extends maface>(arg:T):T{
let length = arg.length
console.log(length);
return arg
}
let result3 = echoLength({length:10})
let result4 = echoLength([1,2])
//泛型接口
interface Ifxjk<T1, T2> {
name: T1;
age: T2;
}
let userFxjk: Ifxjk<string, number> = {
name: "电饭锅",
age: 12
};
//泛型类
class fxle<T1, T2> {
name: T1;
age: T2;
constructor(name: T1, age: T2) {
this.name = name;
this.age = age;
}
}
let fxleue = new fxle<string, number>("小米", 12);//写法一
let fxleue: fxle<string, string> = new fxle("小米", "12");//写法二
//类型参数约束(将泛型T的键值作为K的类型)
function lxca<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
lxca({ a:1,b:2}, "b"); //第二个参数的值只能是a或b
命名空间
创建命名项目:
新建文件夹demo→建src和build和html文件
生成两个配置文件:npm init -y 和 tsc --init
在tsconfig.json中配置
html要引入对应编译后的js文件
命名空间
项目开发过程中,我们会发现我们的命名是有严格规范的,我们不能随意的去起名字,但若是都采用尽量标准化的方式去命名,我们又无法避免的会造成污染,TypeScript提供了namespace 避免这个问题出现
在TS1.5之前被叫做内部模块,主要用于组织代码,避免命名冲突
本质就是定义一个大对象,把变量/方法/类/接口...的都放里面
通过 export 导出
通过 namespace 定义
命名空间.ts
export namespace D {
export const val = 'xxxxxx'
}
//使用.ts
import { D } from '路径'
console.log(D.val)
//使用三斜杆语法导出和导入命名空间
//nameapsce.ts:
namespace ns {
export const val = '1111'
}
//使用
///<reference path="路径.ts" />
console.log( ns.val )
export namespace Components {
export namespace SubComponents {
export class Test {}
}
export class Header {
constructor() {
const elem = document.createElement("div");
elem.innerText = "This is Header";
document.body.appendChild(elem);
}
}
export class Footer {
new Header()
}
}
新建立一个项目TSTest,在桌面新建立一个文件夹,然后在VSCode中打开。
打开终端,输入npm init -y,创建package.json文件
在终端中输入tsc --init,创建tsconfig.json文件
修改tsconfig.json配置rootDir和outDir.
新建src文件夹,在里边建立index.html,page.ts文件
编写index.html文件,并引入page.ts文件
编写page.ts文件。
使用percel打包
index.html 直接引入ts文件:
<script src="./page.ts"></script>
安装:
yarn add --dev parcel@next
package.json:
{
"scripts": {
"test": "parcel ./src/index.html"
},
}
运行:yarn test
忽略引入jq时编译器报错
declare var $: any;
2、声明函数时,要标明返回数据的类型
3、引用类型有 Array、new String、date、正则表达式
4、字符串的方法有.length .indexOf() . lastIndexOf() .剪切.substring("2","3") 替换.repalce("你好","我好")
5、变量的作用域:局部变量 和全局变量。。元祖
6、什么是类,一种事物的集合,有属性和构造函数
7、通过new 一个类生成的实例叫对象
class XiaoJieJie{ //类
name:string;
age:number;
constructor (name:string,age:number){
this.name = name;
this.age = age;
};
say(){
console.log("你好这里是南方航空!")
}
}
let recall:XiaoJieJie = new XiaoJieJie("小红",20) //对象
console.log(recall)
recall.say()
8、修饰符:public(都可访问)、protested(本类和子类可访)、private (本类可访)
9、继承
class JsShuai extends Jspang{
public xingxiang:string = '帅气'
public zhuangQian(){
console.log('一天赚一个小目标')
}
}
let shuai = new JsShuai("技术帅",5,'演讲')
shuai.interst() //使用父类的方法
shuai.zhuangQian() //调用子类自身扩展的方法
10、重写,在子类重写父类的方法,方法名要一样
11、接口 interface Husband{ }
12、命名空间:
namespace shuaiGe{
export class Dehua{
public name:string = '刘德华'
talk(){
console.log('我是帅哥刘德华')
}
}
}
let dehua1:shuaiGe.Dehua = new shuaiGe.Dehua()
dehua1.talk()
13、tsconfig
include:[./src/**/**] //需要编译的文件路径
exclude:[./src/**/**] //不需要编译的文件路径
complieoptions:{
target:'es6',//将ts代码编译成什么语言
strict:true,//所有严格模式的总开关
moudle:'es6',//使用es6的模块化方式
outDir:'./dist/'//编译后端代码位置
allowJs:false,//是否编译js文件
checkJs:false,//是否检查js文件的代码语法
removeComments:true,//是否去除注释编译
noEmit:false//是否生成编译后的js文件
alwaysStrict:false,//编译后的js文件是否启用严格模式
noImplicitAny:false,//是否不能出现any类型
strictNullChecks:true,//是否严格检查空值
}
函数
//匿名函数
const func = function(a:number,b:number):number{ return a+b }
//函数
function func(a:number,b:number){ return a+b }
//箭头函数
const func = (a:number,b:number)=> a+b
//接口函数
type myFunc = (a:number,b:number)=> number
const func:myFunc = (a:number,b:number)=> a+b
//默认参数、可选参数
const func = (a:number=1,b?:number,...arg:any[])=>{}
//构造函数
const myFun = new Function(a,b,'return a*b')
myFun(2,3) //6
//递归函数
function sum(arr:number[],n:number):number{
if(n<=0){
return 0
}else{
return sum(arr,n-1)+arr[n-1]
}
}
//计算数组前2行的总和
sum([1,2,3],2)
函数重载
函数重载:函数名相同,参数、参数的个数、参数的类型,函数的返回类型可不同
用处:某些函数的参数类型或个数不一样,但函数体的写法相同,可以用到重载
function start(s1:string):void
function start(n:number,s1:string):void
function start(x:any,y?:any):any{
console.log(x)
console.log(y)
}
start('小米')
索引类型
//索引类型
let obj1 = {
name: "小红",
age: 16
};
//传入key返回所有对应的value
function getObjForKey<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
let arr = [] as T[K][];
keys.forEach(res => {
arr.push(obj[res]);
});
return arr;
}
console.log("getObjForKey: ", getObjForKey(obj1, ["name", "age"]));
//条件类型
//相当与条件运算符,常用于处理重载函数
interface Inter {
name:string
}
interface Face {
age:number
}
//如果T是string类型,则Contion<T>是 Inter否则 是 Face
type Contion<T> = T extends string ? Inter : Face
function reload<T extends number|string>(val:T):Contion<T>{
throw "
}
//分布式类型:被检测出是联合类型的称为分布式类型
//剔除某个类型
type res = Exclude<string|number|boolean,number> //剔除number
//剔除null或undefined
type res = NonNullable<string|number|undefind> //undefind会被剔除
//获取函数返回值的类型
type res = ReturnType<()=>number>
//将类的构造函数的参数变为元组
type res = ConstructorParameters<typeof Person>
//将函数的参数作为元祖
type res = Parameters<typeof fun>
infer关键字
type ElementOf<T> = T extends Array<infer E> ? E:T
(解析:infer E代表数组的类型)
type res1 = ElementOf<string[]> //则res1是string类型
映射类型
映射类型:将原有的类型映射出新的类型,如将原来型的属性的只读属性去除
interface yspro {
name:string
age:number
}
type newyspro = Readonly<yspro> //为每一个属性加上只读属性
type newyspro2 = Partial<yspro> //为每一个属性加上可选属性
type newyspro3 = Required<yspro> //为每一个属性加上必须要有属性
//Record:
type Name = "xiaomi"|'huawei'
type Person = {
name:string
age:number
}
type newtype = Record<Name,Person>//哪个需要作为键值则放在第一个参数
let res:newtype = {
xiaomi:{
name:'mi'
age:18
},
huawei:{
name:'hua'
age:19
}
}
//Pick:将部分属性类型映射
let res = Pick<yspro,name> //仅映射name属性
//Omit
let res = Pick<yspro,name> //不要映射name属性
//OmitThisParameter:从T中剔除this参数类型
function fo(this:object,x:number){}
type T0 = OmitThisParameter<typeof fo> //(x:number)=>void
type T2 = OmitThisParameter<string> //string 传入什么类型就是什么类型
装饰器
//装饰器是一种特殊类型的声明,它能够被附加到类,方法,访问器,属性或参数上。用 @ 添加
//必须在命令行或 tsconfig.json 里启用 experimentalDecorators 编译器选
//装饰器工厂:如果我们要定制一个修饰器如何应用到一个声明上,我们得写一个装饰器工厂函数。 装饰器工厂就
//是一个简单的函数,它返回一个表达式,
//函数装饰器
//@zsqHs应用于普通函数 target是prototype
//@zsqHs应用于静态函数 target是类的构造函数
function zsqHs(target: any, key: string, desciptor: PropertyDescriptor) {
console.log("desciptor: ", desciptor);
desciptor.value = () => {
return "自定义返回值";
};
}
class Zsq {
constructor(public name: string) {
this.name = name;
}
@zsqHs
showName() {
return this.name;
}
}
let zsq = new Zsq("nihao");
console.log("showName", zsq.showName()); //自定义返回值
//类的属性装饰器(只有两参数)
function sxzsq (target: any, key: string){
}
//类的构造函数的参数装饰器
function cszsq (target: any, key: string,index:number){
}
class Zsq {
name: string
constructor(@cszsq name: string) {
this.name = name;
}
}
//装饰器的使用
function useZsq (msg:string){
return function (target:any,key:string,desciptor: PropertyDescriptor) {
const fn = desciptor.value
try(){
fn()
}carch(e){
console.log(msg)
}
}
}
const testVla:string = "real name"
class Zsq {
name: string
constructor( name: string) {
this.name = name;
}
@useZsq
show(){
return testVla
}
}
模块的导入与导出
JS:
//这里导出和导入名称和不一致
export default xxx
import ooo from '路径'
//导入导出名称需要一致
export const xxx
import { xxx } from '路径'
node模块
exports.xxx = xxx
const xxx = require("路径")
const { xxx,xxx } = require("路径")
module.exports.xxx = xxx
const xxx = require("路径")
const { xxx } = require("路径")
TS模块
方式一:
export const xxx
import { xxx } from '路径'
方式二:
export interface xxx { }
import ooo = require("路径")
//使用 ooo.xxx
描述文件
在开发的过程中引用第三方插件工具的时候,在js里面是可以直接使用它的方法或属性,
但在ts文件里面却用不了且识别不了类型,这个时候就要用到类型描述文件,
以.d.ts为文件后缀名
很多流行的第三方库的声明文件不需要我们定义了
https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types
https;//www.typescriptlang.org/dt/search?search=