TypeScript说属性不存在于类型上,但它显然存在

h7wcgrx3  于 2023-04-13  发布在  TypeScript
关注(0)|答案(1)|浏览(200)

这是注册组件:

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有一个提供程序'email',它将具有errors属性,如果提供程序是其他的,那么它将具有error属性。

mzsu5hc0

mzsu5hc01#

问题是backendError结构化参数的类型BackendError是一个联合类型。因此,Typescript无法确定它是包含error属性的类型还是包含errors属性的类型。
但是 * 你 * 在使用errors之前检查provider === 'email'是否存在,而且你 * 在使用backendError?.error之前检查它是否存在!这应该足以确定backendError的精确类型,对吗?
好吧,是的,从我的Angular 来看。显然不是从TS的Angular 来看。显然,对于第一种情况,TS被provider可以接受包含errors: Partial<EmailSignupDataSchema>的类型的值'email'的事实所迷惑,或者包含error: string的类型的几个其他值之一。如果provider的每个可能值都有一个类型,这是可行的-就像文档中的歧视工会的例子一样。然而,我不知道为什么TS不理解你的方式
对于第二种情况,使用?.测试属性的存在性显然会导致这个错误,因为使用这个操作符需要指定属性的存在性。因此,将代码更改为以下内容应该可以工作:

<SocialSignups
  onSubmit={onSubmit}
  backendError={
    !!backendError && 'error' in backendError ? backendError.error : undefined
  }
/>

你可以对errors做同样的事情,并完成它:

<EmailSignupForm
  onSubmit={onEmailSignupFormSubmit}
  backendError={
    !!backendError && 'errors' in backendError ? backendError.errors : undefined
  }
/>

或者使用类型 predicate 函数:

function isBackendErrorEmail(x?: BackendError<any>): x is {
    provider: 'email';
    errors: Partial<EmailSignupDataSchema>;
} {
    return !!x && x.provider === 'email';
}

并且:

<EmailSignupForm
  onSubmit={onEmailSignupFormSubmit}
  backendError={
    isBackendErrorEmail(backendError) ? backendError.errors : undefined
  }
/>

相关问题