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

TypeScript提示属性不存在但实际已定义的类型问题排查

TypeScript联合类型收窄失败:访问属性报错问题排查与修复

问题描述

定义了BackendError泛型联合类型:当provider为'email'时包含errors属性,其余provider(facebook/google/linkedin)时包含error属性。但在组件中通过backendError?.provider === 'email'进行类型收窄后,访问backendError.errors仍被TypeScript提示“属性'errors'不存在”,访问backendError.error时也有类似报错。

组件代码

import { type IResolveParams } from 'reactjs-social-login';
import { Box, Divider } from '@chakra-ui/react';
import { EmailSignupForm, type EmailSignupDataSchema } from './EmailSignupForm';
import { SocialSignups } from './SocialSignups';
import { SignupLoading } from './SignupLoading';

type SignupProvider = 'email' | 'facebook' | 'google' | 'linkedin';

type SignupData<T extends SignupProvider> = T extends 'email'
  ? {
      provider: 'email';
      data: EmailSignupDataSchema;
    }
  : IResolveParams;

type BackendError<T extends SignupProvider> = T extends 'email'
  ? {
      provider: 'email';
      errors: Partial<EmailSignupDataSchema>;
    }
  : {
      provider: Omit<SignupProvider, 'email'>;
      error: string;
    };

type SignupProps = {
  onSubmit: (data: SignupData<SignupProvider>) => void;
  isLoading?: boolean;
  backendError?: BackendError<SignupProvider>;
};

export const Signup = ({
  onSubmit,
  isLoading,
  backendError,
}: SignupProps): JSX.Element => {
  const onEmailSignupFormSubmit = (data: EmailSignupDataSchema): void => {
    onSubmit({
      provider: 'email',
      data,
    });
  };

  return (
    <Box
      minW={{ base: '90%', md: '468px' }}
      backgroundColor="whiteAlpha.900"
      boxShadow="md"
      px={5}
      py={8}
      position="relative"
    >
      {isLoading === true ? <SignupLoading /> : null}
      <EmailSignupForm
        onSubmit={onEmailSignupFormSubmit}
        backendError={
          backendError?.provider === 'email' ? backendError.errors : undefined
        }
      />
      <Divider />
      <SocialSignups
        onSubmit={onSubmit}
        backendError={
          backendError?.error != null ? backendError.error : undefined
        }
      />
    </Box>
  );
};

报错信息

Property 'errors' does not exist on type '{ provider: "email"; errors: Partial<{ email: string; password: string; confirmPassword: string; captchaToken: string; }>; } | { provider: Omit<SignupProvider, "email">; error: string; }'.
  Property 'errors' does not exist on type '{ provider: Omit<SignupProvider, "email">; error: string; }'.

访问backendError.error时会出现类似的“属性不存在”报错。


问题根源

问题出在泛型条件类型的分发与可辨识联合类型的定义错误:

  1. 当使用BackendError<SignupProvider>时,泛型T是联合类型SignupProvider,条件类型会对联合类型的每个成员进行分发,最终生成的类型是BackendError<'email'> | BackendError<'facebook'> | BackendError<'google'> | BackendError<'linkedin'>;
  2. 但BackendError<Omit<SignupProvider, 'email'>>(非email分支)返回的类型是{ provider: Omit<SignupProvider, 'email'>; error: string; },这里的provider是联合类型,而非单个字面量类型;
  3. TypeScript的可辨识联合类型要求判别式(此处为provider)必须是字面量类型或字面量类型的联合,且每个分支的判别式值唯一绑定对应的属性。由于非email分支的provider是联合类型,TypeScript无法通过provider === 'email'准确收窄到对应的分支类型,导致属性访问报错。

修复方案

将BackendError从泛型类型改为直接定义的可辨识联合类型,确保每个分支的provider是具体的字面量类型,让TypeScript能正确识别判别式:

修改后的类型定义

type SignupProvider = 'email' | 'facebook' | 'google' | 'linkedin';
// 提取非email的provider类型
type SocialProvider = Exclude<SignupProvider, 'email'>;

// 直接定义可辨识联合类型,无需泛型
type BackendError =
  | {
      provider: 'email';
      errors: Partial<EmailSignupDataSchema>;
    }
  | {
      provider: SocialProvider;
      error: string;
    };

// 更新SignupProps中的backendError类型
type SignupProps = {
  onSubmit: (data: SignupData<SignupProvider>) => void;
  isLoading?: boolean;
  backendError?: BackendError; // 去掉泛型参数
};

修复原理

修改后的BackendError是标准的可辨识联合类型:

  • 第一个分支的provider是字面量'email',绑定errors属性;
  • 第二个分支的provider是SocialProvider('facebook'|'google'|'linkedin'),绑定error属性;
    TypeScript能通过backendError?.provider === 'email'准确推断出当前类型是第一个分支,允许访问errors;反之则推断为第二个分支,允许访问error。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:55:32