普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月13日首页

TypeScript 类型推导还可以这么用?

2026年1月13日 17:03

一、为什么需要TypeScript类型推导

肯定是为了节省我们的代码,减少冗余,下面举一个例子,写一个递减函数

下面代码中,写了三个类型都是number,那么我们是不是可以思考,如何减少冗余呢,ts给咱们提供了泛型

正常书写

const desc = (a: number): number => {
    return --a
}
desc(2) // 1

泛型

const desc = <T>(a: T): number => {
    return --a
}
desc(3) // 2

这个例子在项目中的应用场景可谓之少之又少,也可以说,压根不可能出现。
那么衍生出一个问题:有没有更好的方法,自动推导我特定的入参返回特定类型。

有的兄弟,有的!!

我先把公共代码抽出来

// 公共属性
type Pet = {
    name: string
    age: number
}

// 映射类型
type OptionsMap = {
    dog?: {
        bark: string
        barkVolume: number
    } & Pet

    cat?: {
        favoriteToy: string
        furLength: 'short' | 'medium' | 'long'
    } & Pet
}

场景:

  • 组件封装,表单、表单组件类型封装,例如通过表单项type来确定是input、select...就可以明确props类型了
  • 业务逻辑封装,在重复的业务逻辑中我们通常会抽离,通过key去调用,那么对应的参数不同,也会用到泛型、自动推导

二、映射 + 类型推导

我们可以通过映射、类型推导相互配合就能得到特定的类型了,废话少说,上代码

2.1. 先来一个函数案例,通过参数1推导出参数2


const fn = <T extends keyof OptionsMap>(type: T, props: OptionsMap[T]) => {}

fn('cat', {}) 
/*
类型“{}”的参数不能赋给类型“{ favoriteToy: string; furLength: "short" | "medium" | "long"; } & Pet”的参数。  
类型“{}”缺少类型“{ favoriteToy: string; furLength: "short" | "medium" | "long"; }”中的以下属性: favoriteToy, furLengthts-plugin(2345)
*/ 

  • 上面代码声明了宠物映射属性,通过函数的第一个参数去映射中自动匹配第二个参数
  • 恭喜你现在已经掌握类型推导的基础了

2.2. 下面我们再来一个写一个生成集合,里面数据项都是通过推导出来的

type SinglePet<T extends keyof OptionsMap> = {
    type: keyof OptionsMap
    options: OptionsMap[T]
}

type Pets = SinglePet<keyof OptionsMap>[]

const petHome: Pets = [
    {
        type: 'dog',
        options: {
            name: '旺财',
            age: 2,
            bark: '汪汪汪',
            barkVolume: 2,
        },
    },
    {
        type: 'cat',
        options: {
            name: '小白',
            age: 1,
            favoriteToy: '玩具鸟',
            furLength: 'short',
        },
    },
]

数组里的每一项options,我们都是通过type推导出来的。


三、extends + 类型推导

extends和映射作用都是为了自动推导出另一个属性或者另一个类型,作用是一样的,具体使用哪种,可以根据项目而定


type PetKeys = 'dog' | 'cat' | 'fish' | 'bird'

type PetOptions<T extends PetKeys> = T extends 'dog'
    ? OptionsMap['dog']
    : T extends 'cat'
      ? OptionsMap['cat']
      : {
            text: number
        }

type OptionItem<T extends PetKeys> = PetOptions<T>

type Item<T extends PetKeys = PetKeys> = {
    type: T
    options: OptionItem<T>
}

type PetHome = Item[]

const petHome: PetHome = [
    {
        type: 'dog',
        options: {
            name: '旺财',
            age: 2,
            bark: '汪汪汪',
            barkVolume: 10,
        },
    },
    {
        type: 'bird',
        options: {
            text: 1,
        },
    },
    {
        type: 'cat',
        options: {
            name: '小白',
            age: 2,
            favoriteToy: '玩具鸟',
            barkVolume: 5,
            furLength: 'long',
        },
    },
]

上面这段代码主要是使用extend判断对应的类型,在项目中封装很常见,也利于后期拓展,其中核心代码为

type Item<T extends PetKeys = PetKeys> = { 
    type: T options: OptionItem<T> 
}

四、总结

类型推导可以不用,不能不会,有ts的项目必出现这种场景,当然也可以用AI 😂

最后告诉大家几个泛型、推导的注意事项

泛型参数,也就是本文中的T,如果一个使用项中的T已经明确类型了,其他地方也会跟着明确。
泛型参数一旦确定就不会再变,所有咱们如果使用默认值时,引用的类型必须要传递,否则就是按默认值算了。

最后祝大家日入过万,给俺点点关注、点点赞

昨天以前首页

推荐几个国外比较流行的UI库(上)

2026年1月10日 14:45

1、Tailwind CSS

现在写样式的时候,我基本已经离不开 Tailwind CSS 了。最开始接触它的时候,其实挺不适应的,感觉类名又多又杂,全写在标签上。但真正用顺了之后,反而不太想回到以前那种来回切 CSS 文件的方式。

我比较喜欢的是它处理响应式的方式,断点直接写在类名前面,逻辑非常直观。页面在不同尺寸下怎么变,一眼就能看出来。再加上配置文件可以统一管颜色、间距、字体这些东西,对我来说维护起来反而更轻松。

缺点当然也有,比如结构看起来不那么“干净”,但这个在我这里已经不算什么问题了。

下面我们来实现一个瀑布流

<div class="columns-3 ..."> 
    <img class="aspect-3/2 ..." src="/img/mountains-1.jpg" /> 
    <img class="aspect-square ..." src="/img/mountains-2.jpg" /> 
    <img class="aspect-square ..." src="/img/mountains-3.jpg" /> 
    <!-- ... -->
</div>

效果 image.png


2、Bootstrap

虽然 Bootstrap 已经很多年了,但说实话,在一些需求明确、节奏比较快的项目里,它依然很好用。栅格、常见组件基本都有,直接拼就能出页面,几乎不用想太多。

image.png


3、Foundation

Foundation 是一个开源的响应式前端框架,用于构建结构清晰、视觉一致的网页界面。它提供了完整的工具体系,包括响应式网格系统、设计模板,以及基于 HTML、CSS 和 SASS 的样式方案。同时,框架内置了按钮、导航、表单、排版等常见 UI 能力,并支持通过 JavaScript 扩展进一步增强交互功能。

Foundation 采用移动优先的设计理念,与 Bootstrap 类似,布局从小屏设备开始构建,再逐步扩展到更大的屏幕尺寸。这种方式使页面能够自然适配不同设备,无需额外处理复杂的适配逻辑,从而在手机、平板和桌面端之间保持一致且流畅的体验。

在布局层面,Foundation 提供了基于 Flexbox 的 12 列响应式网格系统。页面结构可以通过行与列的组合快速搭建,而网格系统会自动处理不同断点下的尺寸变化与内容堆叠,使整体布局保持简洁、直观且易于维护。

Foundation 的工具包体系也是其重要特性之一。框架内置了可直接使用的网页与邮件组件,使项目在启动阶段不必从零搭建基础结构。这种方式在多平台场景下有助于维持统一的视觉风格,并显著减少重复性工作。

在灵活性方面,Foundation 并未强制绑定特定的设计语言或样式规范。默认配置可根据项目需求进行调整或覆盖,从而在不受框架限制的前提下实现定制化界面设计。这种设计思路在效率与自由度之间取得了较好的平衡。

从整体特性来看,Foundation 对无障碍访问和移动优先设计的重视,使其在构建现代化、包容性网页体验时具有明显优势。模块化架构与 SASS 集成提升了组件定制的效率,也使复杂布局的原型构建更加顺畅。

相对而言,Foundation 的学习成本高于 Bootstrap 等更大众化的方案,对初学者存在一定门槛。此外,其社区规模和生态资源不及 Tailwind 和 Bootstrap 丰富,可直接复用的第三方资源相对有限。在功能完整度较高的同时,对于体量较小的项目而言,可能会引入不必要的复杂度。

特点

1.  **响应式**:先做好手机,再适配平板和电脑。
2.  **网格灵活**:12 列 Flexbox 布局,布局复杂也能处理好。
3.  **组件齐全**:带 JS 插件,交互也有现成的(弹窗、菜单等)。

适合谁:想快速搭复杂页面,有交互,又想用框架自带组件的人。

缺点:学习稍复杂,功能多了,小项目可能显得重。

下面我们来使用它的按钮样式

<!-- Anchors (links) --> 
<a href="about.html" class="button">Learn More</a> 
<a href="#features" class="button">View All Features</a> 

<!-- Buttons (actions) --> 
<button class="submit success button">Save</button> 
<button type="button" class="alert button">Delete</button>

效果

image.png


4、Bulma

Bulma 是那种一看就懂、上手很快的框架。类名语义清楚,布局基于 Flexbox,用起来很顺。

它不依赖 JavaScript 这一点,拿来配合任何技术栈都很方便。不过也正因为这样,一些交互相关的东西需要自己补,这点在用之前心里要有预期。

特点

1.  **响应式**:移动优先,Flexbox 网格布局。
2.  **轻量**:按钮、卡片、表单都有样式,但没有 JS。
3.  **易用**:学习成本低,改样式很方便。

适合谁:只需要快速搭页面、布局和样式固定、不需要框架自带交互的人。

缺点:没有交互组件,复杂行为要自己写。

下面我们来写一个简单的表单

<form class="box">
  <div class="field">
    <label class="label">Email</label>
    <div class="control">
      <input class="input" type="email" placeholder="e.g. alex@example.com" />
    </div>
  </div>

  <div class="field">
    <label class="label">Password</label>
    <div class="control">
      <input class="input" type="password" placeholder="********" />
    </div>
  </div>

  <button class="button is-primary">Sign in</button>
</form>

效果

image.png


❌
❌