You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态访问同一对象中两个不同值时出现TypeScript错误

TypeScript动态索引对象时的类型错误解决方案

错误信息

Element implicitly has an 'any' type because expression of type 'levelTypes | semanticTypes' can't be used to index type '{ level1: { highEmphasis: string; mediumEmphasis: string; outlineHighEmphasis: string; outlineMediumEmphasis: string; semantic: { info: string; onInfo: string; negative: string; onNegative: string; ... 12 more ...; AIGradientEndpoint: string; }; interactive: { ...; }; }; ... 15 more ...; aiIntense: { ...; }; } | { ....'.

  Property 'level1' does not exist on type '{ level1: { highEmphasis: string; mediumEmphasis: string; outlineHighEmphasis: string; outlineMediumEmphasis: string; semantic: { info: string; onInfo: string; negative: string; onNegative: string; ... 12 more ...; AIGradientEndpoint: string; }; interactive: { ...; }; }; ... 15 more ...; aiIntense: { ...; }; } | { ....'.ts(7053)

问题场景

构建组件库时,动态访问调色板对象的双层键触发上述错误,出错代码片段:

theme.fdsTheme.palette[getForeground(level)][level].interactive.highEmphasisSurface

完整组件代码:

import styled from "@emotion/styled";

type levelTypes =
  | "level1"
  | "level2"
  | "level3"
  | "level4"
  | "level5"
  | "level6"
  | "level7";

type semanticTypes =
  | "negative"
  | "negativeIntense"
  | "critical"
  | "criticalIntense"
  | "positive"
  | "positiveIntense"
  | "info"
  | "infoIntense"
  | "attention"
  | "attentionIntense"
  | "ai"
  | "aiIntense";


interface ButtonStyleProps {
  level: levelTypes | semanticTypes;
}


const isNavigation = (level: levelTypes | semanticTypes): boolean => {
  return level === "level6" || level === "level7";
};

const getForeground = (level: levelTypes | semanticTypes)=> {
  if (isNavigation(level)) {
    return "onNavigation";
  } else {
    return "onSurface";
  }
};

const StyledButton = styled(BaseButton)<ButtonStyleProps>(({ theme, level }) => {

  return {
    [`&.${buttonClasses.root}`]: {
      // 其他样式...

      "&.fds-Button-primary": {
        backgroundColor:
          theme.fdsTheme.palette[getForeground(level)][level].interactive.highEmphasisSurface,
        color:
          theme.fdsTheme.palette[getForeground(level)][level].interactive.onHighEmphasis,
        border: `0px solid ${
          theme.fdsTheme.palette[getForeground(level)][level].interactive.highEmphasisOutline
        }`,
      },
    },
  };
});

已尝试的方法

尝试定义Theme类型并使用类型断言,但问题未解决:

扩展Emotion的Theme类型

// 定义自定义Theme类型
import "@emotion/react";
import { Theme } from "../theme";

type DefaultTheme = typeof Theme;

declare module "@emotion/react" {
  export interface Theme extends DefaultTheme {}
}

使用类型断言修改组件代码

import { Theme } from "@emotion/react";


// 之前的代码 ...


const StyledButton = styled(BaseButton)<ButtonStyleProps>(({ theme, level }) => {

  return {
    [`&.${buttonClasses.root}`]: {
      // 其他样式...

      "&.fds-Button-primary": {
        backgroundColor:
          theme.fdsTheme.palette[getForeground(level) as keyof Theme][level as keyof Theme].interactive.highEmphasisSurface,
      },
    },
  };
});

简化示例

为便于定位问题,提供简化后的测试代码,错误出现在theme[checkLevel(levelTwo)][levelTwo].color;行:

interface TestProps {
  levelOne: "color" | "typo";
  levelTwo: "level1" | "level2" | "level3";
}

function TestComponent({ levelOne, levelTwo }: TestProps) {
  const theme = {
    color: {
      level1: {
        color: "",
      },
      level2: {
        color: "",
      },
    },
    typo: {
      level1: {
        color: "",
      },
      level2: {
        color: "",
      },
      level3: {
        color: "",
      },
    },
  };

  const checkLevel = (
    level: "level1" | "level2" | "level3",
  ): "color" | "typo" => {
    return level === "level1" || level === "level2" ? "color" : "typo";
  };

  const c = theme[checkLevel(levelTwo)][levelTwo].color;

  console.log(c);
}

解决方案

原因分析

TypeScript无法推断getForeground(level)返回的键与level之间的关联关系,导致认为level可能不存在于theme.fdsTheme.palette[getForeground(level)]对应的对象中,从而抛出索引类型错误。

方案1:使用类型守卫缩小范围

通过类型守卫明确level与返回的前景键之间的对应关系,让TypeScript能准确推断类型:

针对原组件代码修改:

// 定义类型守卫
const isNavigationLevel = (level: levelTypes | semanticTypes): level is "level6" | "level7" => {
  return level === "level6" || level === "level7";
};

const StyledButton = styled(BaseButton)<ButtonStyleProps>(({ theme, level }) => {
  let bgColor: string;
  let textColor: string;
  let borderColor: string;

  if (isNavigationLevel(level)) {
    const palette = theme.fdsTheme.palette.onNavigation[level].interactive;
    bgColor = palette.highEmphasisSurface;
    textColor = palette.onHighEmphasis;
    borderColor = palette.highEmphasisOutline;
  } else {
    const palette = theme.fdsTheme.palette.onSurface[level].interactive;
    bgColor = palette.highEmphasisSurface;
    textColor = palette.onHighEmphasis;
    borderColor = palette.highEmphasisOutline;
  }

  return {
    [`&.${buttonClasses.root}`]: {
      "&.fds-Button-primary": {
        backgroundColor: bgColor,
        color: textColor,
        border: `0px solid ${borderColor}`,
      },
    },
  };
});

方案2:使用泛型明确键关联

通过泛型函数让TypeScript明确返回键与输入level的对应关系:

针对原组件代码修改:

// 定义泛型版本的getForeground
type NavigationLevels = "level6" | "level7";
type NonNavigationLevels = Exclude<levelTypes | semanticTypes, NavigationLevels>;

const getForeground = <T extends levelTypes | semanticTypes>(level: T): T extends NavigationLevels ? "onNavigation" : "onSurface" => {
  return isNavigation(level) ? "onNavigation" : "onSurface" as any;
};

const StyledButton = styled(BaseButton)<ButtonStyleProps>(({ theme, level }) => {
  return {
    [`&.${buttonClasses.root}`]: {
      "&.fds-Button-primary": {
        backgroundColor:
          theme.fdsTheme.palette[getForeground(level)][level].interactive.highEmphasisSurface,
        color:
          theme.fdsTheme.palette[getForeground(level)][level].interactive.onHighEmphasis,
        border: `0px solid ${
          theme.fdsTheme.palette[getForeground(level)][level].interactive.highEmphasisOutline
        }`,
      },
    },
  };
});

方案3:重构调色板类型定义

如果有权限修改Theme的类型定义,可以明确palette中每个键对应的子键类型:

// 定义调色板的类型
interface Palette {
  onSurface: Record<NonNavigationLevels, {
    interactive: {
      highEmphasisSurface: string;
      onHighEmphasis: string;
      highEmphasisOutline: string;
    }
  }>;
  onNavigation: Record<NavigationLevels, {
    interactive: {
      highEmphasisSurface: string;
      onHighEmphasis: string;
      highEmphasisOutline: string;
    }
  }>;
}

// 在Theme类型中使用这个Palette接口
interface Theme {
  fdsTheme: {
    palette: Palette;
  }
}

这样TypeScript就能准确判断onSurface和onNavigation对应的子键范围,避免索引错误。


内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 17:49:55