TS基础2(泛型、枚举、元组)-学习笔记

本文深入介绍了TypeScript中的泛型,包括其基本用法、泛型约束及在函数和类中的应用。同时,枚举类型的使用也被详细阐述,展示了如何通过枚举来方便地处理特定值。此外,还讲解了元组的概念,它允许在一个数组中存储不同类型的值。通过示例代码,帮助读者更好地理解和运用这些概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TS基础2(泛型、枚举、元组)-学习笔记

在这里插入图片描述

泛型

    //泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
    //<T>   泛型变量T T表示什么?任何类型
    function hello<T>(name:T):T{   //hello函数是泛型
        return name
    };
    //hello<string>('abc');
    hello<string|number>(123);  //定义多个类型
    hello(123);  //类型推断  编译器会根据传入的参数来自动的判断T的类型

    //缺点  只能传string
    // function hello2(name:string):string{
    //     return name
    // };
    // hello2(123);
    //缺点 不确定返回值的类型
    function hello3(name:any):any{
        return name
    };
    hello3(123);

    //泛型在函数中具体的写法
    //函数声明
    function f<T>(x:T):T{
        return x
    };
    //函数表达式
    let f2 = function<U>(x:U):U{
        return x
    }
    //ES6
    let f3 = <U>(x:U):U=>{
        return x
    }

    //限制  参数的属性或方法一定是属于任何类型
    function hello4<T>(name:T):number{   //hello函数是泛型
        //name 表示是任何类型
        return name.length  //error  T表示为任何类型  也就是name.length是所有类型都支持的属性
    };

    function hello5<T>(name:T[]):number{   //number[]   string[]
        //name 数组
        return name.length   //
    };
    //hello5<number>([1,2,3])
    hello5<string >(['1','2','3'])


    //泛型的约束 extends
    //<T extends Abc> 泛型T必须符合接口ABC的形状
    interface LengthN{
        length:number
    }

    function hello6<T extends LengthN>(name:T):number{   //hello函数是泛型
        //name 必须有length属性的类型 
        return name.length  
    };
    hello6<string>('222');

    //数组泛型     类型+[]
    let arr:Array<number> =[1,2,3];
    var arr2:number[] = [1,2,3];

    //需求  创建一个数组   传入3表示数组的长度为3  具体'x'   ['x','x','x']
    //createArray(长度,值) 
    //createArray(5,'a')    ['a','a','a','a','a']
    //createArray(2,3)    [3,3]
    function createArray(length:number,value:any):Array<any>{
        var arr = [];
        for(let i=0;i<length;i++){
            arr[i] = value
        };
        return arr;
    };
    createArray(5,'a') ;

    function createArray2<T>(length:number,value:T):Array<T>{
        var arr = [];
        for(let i=0;i<length;i++){
            arr[i] = value
        };
        return arr;
    };
    createArray2<number>(5,3) ;

    //多个类型参数
    function f6<N,S>(a:[N,S]):[S,N]{
        return [a[1],a[0]]
    };
    f6<number,string>([1,'a'])

    //泛型在类中的运用
    class A2<T>{
        n:T;
        constructor(num:T){
            this.n = num
        }
        action(x:T):T{
            return x
        }
    };
    var a2 = new A2<number>(1);

枚举

 	//枚举 enum类型是对JavaScript标准数据类型的一个补充
    var arr7 = ['a','b','c'];
    arr7['a']  //不能通过键来获取,只能索引
    var obj = {a:1,b:2};
    obj[1] //不能通过索引取值

    enum Color {red,green,blue,'a','b','c'};
    var c:Color= Color.red //0  通过键取值
    var c2:string = Color[1] //green  通过索引取值

    //改变索引值
    enum Color2 {red=10,green,blue};
    var c3:Color2= Color2.green  //11

    //手动设置
    enum Color3 {red=30.7,green=20,blue=10};
    var c4:Color3= Color3.green //20 

    //任意值
    var c11 = 'hello';
    enum Color4 {red=30.7,green=<any>c11,blue=c11.length};
    var c5:Color4= Color4.green //hello 

元组

	//元组  数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。
    //数组
    var lists:number[] = [1,2,3,4,5];
    var lists2:Array<string> = ['1','2'];
    var lists3:Array<any> = ['1','2',true,1,2];
    var t:[number,string] = [10,'a'];  //元组允许一个数组中可以有多个不同的类型  一一匹配

例子参考

//档位
enum Gear {
    First=1, Second=3, Third=5
}
enum Color {
    White, Red
}
//接口
interface Drivable {
    //启动
    start(): void;
    //驾驶
    drive(time: number, speed: Gear): void;
    //行驶距离
    getKilometer(): number
}
//定义一个抽象的类  
//abstract  抽象的类  没有实现  不可被实例化
//定义动物类
// abstract class Animal {
//     abstract eat():void;  //抽象类的子类必须实现抽象类的抽象方法
// }
// //var a = new Animal();  //error    用到了abstract修饰符,不能被实例化 
// class Dog extends Animal{
//     constructor(){
//         super();
//     }
//     //抽象类的子类必须实现抽象类的抽象方法
//     eat(){
        
//     }
// }

abstract class Car implements Drivable {  //定义一个抽象类  来实现Drivable接口
    protected _isRunning: boolean;   //只能被子类访问的属性
    protected _distanceFromStart: number;  //只能被子类访问的属性

    constructor() {
        this._isRunning = false;
        this._distanceFromStart = 0;
    }

    public start() {    //公共启动汽车
        this._isRunning = true;
    }
    //抽象类的子类必须实现抽象类的抽象方法
    abstract drive(time: number, speed: Gear): void;   
    public getKilometer(): number {    //公共行驶距离
        return this._distanceFromStart;
    }
}
//派生类   子类
class BMW<T extends Color> extends Car {
    private color: Color;  //私有属性

    constructor(T) {
        super();
        this.color = T;
    }

    public drive(time: number, speed: Gear): void {
        if (this._isRunning) {
            this._distanceFromStart += time*speed;//行驶距离
        }
    }

    public getColor(): string {
        return Color[this.color]
    }
}

let bmw = new BMW(Color.Red);
bmw.start();
bmw.drive(10, Gear.First);
bmw.drive(60, Gear.Third);
document.body.innerHTML = "distance:"+bmw.getKilometer()+',color:'+bmw.getColor()
<think>好的,用户之前问过如何在TypeScript中使用useContext,我之前已经给了一个比较详细的回答,现在用户又提到了站内引用中的一些内容,可能想更具体地了解如何结合类组件和函数组件的用法,或者插槽的部分。不过用户这次的问题主要还是关于如何在TS中创建和使用useContext,所以需要确保回答覆盖基本步骤,同时注意类安全。 首先,用户引用的内容里提到了React.createContext、Provider和Consumer的使用方法,但用户可能混淆了useContext和Consumer的写法,特别是在TS中的类处理。需要指出在函数组件中使用useContext替代Consumer的情况,同时强调类定义的重要性。 接下来,我需要回顾之前的回答,确保不重复但更简洁。用户可能希望看到明确的步骤,比如创建带类的Context、提供Provider、使用useContext消费数据,以及处理未提供Provider的情况。同时,用户提到的类组件可能在TS中也需要类注解,但用户主要关注的是函数组件和hooks,所以可能需要简要说明类组件中的ContextType用法。 另外,用户引用的部分提到插槽,但实际可能与Context无关,可能是引用中的混淆,所以可以忽略这部分,专注于Context的正确用法。需要检查是否有必要提到useReducer结合Context的状态管理,因为之前的回答已经包含,但用户可能希望更基础的步骤。 最后,确保示例代码中的类定义正确,比如使用React.createContext<T>,处理默认值,以及在子组件中的类推断。还要注意用户可能的误区,比如未提供Provider时如何处理类安全,使用非空断言或者条件判断。需要强调类复用和最佳实践,比如使用interface定义复杂类,或者使用typeof获取已有对象的类。</think>以下是 TypeScript 中 `useContext` 的完整使用指南,特别针对类安全场景优化: --- ### 一、创建类化 Context(核心步骤) 1. **定义数据类** 使用 `type` 或 `interface` 明确数据结构: ```typescript type ThemeContextType = { theme: "light" | "dark"; toggleTheme: () => void; } ``` 2. **初始化默认值** 确保初始值完全匹配类定义: ```typescript const defaultTheme: ThemeContextType = { theme: "light", toggleTheme: () => console.warn("未找到ThemeProvider") }; ``` 3. **创建 Context 对象** 通过 `<Type>` 声明上下文类: ```typescript const ThemeContext = React.createContext<ThemeContextType>(defaultTheme); ``` --- ### 二、类安全的 Provider 实现 在父组件中通过 `useState` + `useMemo` 优化类传递: ```typescript function App() { const [theme, setTheme] = useState<"light" | "dark">("light"); // 使用 useMemo 避免不必要的重渲染 const contextValue = useMemo(() => ({ theme, toggleTheme: () => setTheme(prev => prev === "light" ? "dark" : "light") }), [theme]); return ( <ThemeContext.Provider value={contextValue}> <ChildComponent /> </ThemeContext.Provider> ); } ``` --- ### 三、消费 Context 的两种方式 #### 方式 1:函数组件写法(推荐) ```typescript function ChildComponent() { // 自动获得 ThemeContextType 类推断 const { theme, toggleTheme } = useContext(ThemeContext); return ( <div style={{ backgroundColor: theme }}> <button onClick={toggleTheme}>切换主题</button> </div> ); } ``` #### 方式 2:类组件写法(兼容旧代码) ```typescript class ClassChild extends React.Component { // 必须声明 contextType static contextType = ThemeContext; declare context: React.ContextType<typeof ThemeContext>; render() { const { theme, toggleTheme } = this.context; return <div>{theme}</div>; } } ``` --- ### 四、特殊场景处理 #### 1. 无默认值的 Context ```typescript // 创建时允许 undefined const AuthContext = React.createContext<User | undefined>(undefined); // 使用时校验存在性 function ProtectedComponent() { const user = useContext(AuthContext); if (!user) throw new Error("需要AuthProvider"); return <div>{user.name}</div>; } ``` #### 2. 复杂对象类 使用 `interface` 定义嵌套结构: ```typescript interface AppContext { settings: { theme: "light" | "dark"; language: "zh" | "en"; }; updateSettings: (newSettings: Partial<AppContext['settings']>) => void; } ``` --- ### 五、最佳实践 1. **类复用技巧** 通过 `typeof` 自动推导类: ```typescript const defaultConfig = { fontSize: 16, darkMode: false }; type ConfigType = typeof defaultConfig; // 自动推导类 ``` 2. **类断言处理** 当确定 Provider 存在时使用非空断言: ```typescript const { theme } = useContext(ThemeContext)!; ``` 3. **错误边界处理** 创建自定义 Hook 增强安全性: ```typescript function useTheme() { const context = useContext(ThemeContext); if (!context) { throw new Error("useTheme 必须在 ThemeProvider 内使用"); } return context; } ``` --- ### 常见问题解答 **Q:为什么需要给 createContext 参数?** A:TypeScript 需要通过明确上下文数据类,否则会推断为 `any` 类失去类检查能力[^1] **Q:如何避免 Provider 的 value 属性类错误?** A:使用 `useMemo` 包裹对象可同时解决类校验和性能优化两个问题[^3] **Q:Consumer 组件在 TS 中如何声明类?** A:已不推荐使用 Consumer,建议直接使用 `useContext` Hook 获得更好的类推断 --- 通过遵循这些 TypeScript 最佳实践,可确保 Context API 在获得完整类提示的同时,避免常见的运行时错误。实际项目建议结合 ESLint 的 `@typescript-eslint/no-non-null-assertion` 规则控制非空断言的使用频率[^2]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值