Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Typescript中的--strictNullChecks从DB或API接收异步数据的变量的类型?_Typescript_Asynchronous_Typescript Typings_Type Assertion - Fatal编程技术网

使用Typescript中的--strictNullChecks从DB或API接收异步数据的变量的类型?

使用Typescript中的--strictNullChecks从DB或API接收异步数据的变量的类型?,typescript,asynchronous,typescript-typings,type-assertion,Typescript,Asynchronous,Typescript Typings,Type Assertion,我的React+Typescript项目遇到了一些问题 每当我必须键入一个以null开头并将接收某种类型的async数据的变量时,就会发生这种情况 例如:我有一个管理员用来创建和编辑帖子的AdminBlogPostPage 我有一个变量,用于保存blogPost数据 但是当该页面第一次呈现时,仍然没有任何可用数据,因为帖子将被加载async 目前,我是这样输入的(我使用的是--strictNullChecks): blogPost状态从null开始,一旦加载,它就会变成blogPost 它很好用

我的React+Typescript项目遇到了一些问题

每当我必须键入一个以
null
开头并将接收某种类型的
async
数据的变量时,就会发生这种情况

例如:我有一个管理员用来创建和编辑帖子的
AdminBlogPostPage

我有一个变量,用于保存
blogPost
数据

但是当该页面第一次呈现时,仍然没有任何可用数据,因为帖子将被加载
async

目前,我是这样输入的(我使用的是
--strictNullChecks
):

blogPost
状态从
null
开始,一旦加载,它就会变成
blogPost

它很好用。但是从这一点开始,管理员对
blogPost
对象所做的一切都变得有点痛苦,因为Typescript总是“担心”blogPost可能是
null

在这些情况下,我通常使用Redux,因此在我的
reducer
中,我经常要做类型断言,让Typescript清楚地知道,当该操作发生时,
blogPost
将是
blogPost
而不是
null

例如:

/* ############################# */
/* #### BLOGPOST PROPERTIES #### */
/* ############################# */

UPDATE_CATEGORY(state, action:UPDATE_CATEGORY) {
  const blogPost = state.blogPost as TYPES.BLOGPOST;   // TYPE ASSERTION HERE
  const { value } = action.payload;
  blogPost.category = value;
},
UPDATE_BOOLEAN_PROPERTY(state, action: UPDATE_BOOLEAN_PROPERTY) {
  const blogPost = state.blogPost as TYPES.BLOGPOST;   // TYPE ASSERTION HERE
  const { name, value } = action.payload;
  blogPost[name] = value;
},
UPDATE_STRING_PROPERTY(state, action: UPDATE_STRING_PROPERTY) {
  const blogPost = state.blogPost as TYPES.BLOGPOST;   // TYPE ASSERTION HERE
  const { name, value } = action.payload;
  blogPost[name] = value;
},
UPDATE_PRODUCT_CATEGORY(state, action: UPDATE_PRODUCT_CATEGORY) {
  const blogPost = state.blogPost as TYPES.BLOGPOST;   // TYPE ASSERTION HERE
  const { value } = action.payload;
  blogPost.productCategory = value;
},
人们通常如何处理
async
数据的类型?在数据到达之前从
null
开始是否值得?还是最好从一个有效的
blogPost:blogPost
存根开始作为初始状态,这样Typescript就会知道
blogPost
始终是一个
blogPost
,我就不必再处理这种重复的类型断言了


注意:即使我决定从有效的
blogPost:blogPost
开始,我仍然需要从数据库加载数据。将不使用初始状态。它将允许我使用
blogPost:blogPost
而不是
null | blogPost
这通常是一个令人讨厌的问题。这可能不足以以你喜欢的方式解决你的问题,但我将把我的答案留在这里,希望它能帮助任何人

根据我的经验,我一直采用以下两种方法之一处理此问题(其中一种依赖于框架):

  • 使用更简洁的编译时类型断言,例如
    (而不是显式强制转换)在绝对确定值为非空/非未定义的情况下。例如,尽管运行时
    state.blogPost
    可能为空,但这将编译:

    state.blogPost![name] = value
    state.blogPost!.property = value
    
    这断言
    state.blogPost
    值为非null,应该可以正常编译。请注意,此运算符在编译期间被擦除,并且仅用于编译时类型检查。当确认值的类型可能为空时,应使用该运算符,但在运行时不应使用该运算符。如果该值在运行时为空,那么它当然会通过一个错误返回。此外,在某些特定的场景中也可能有用

  • 对于某些React框架,例如(SSR+CSR),可以从API异步获取呈现页面的初始属性,并且在使用props解析
    承诺之前,页面不会完成其呈现。例如,这涵盖了这个场景

    这是使用Next.js的特定示例,但其基本思想是在注入组件属性和呈现之前获取博客文章,并且在运行时和编译时应为非null



与您的问题无关,请记住您使用方括号表示法
x[y]
的用例以及任何安全隐患。请参阅这篇GitHub文章,解释附带情况下的潜在安全问题:

我将创建一个单独的类型来表示尚未从后端/数据库加载数据的状态,而不是使用
null

type UnloadedBlogPost {
  stateKind: "unloaded";
}

type LoadedBlogPost {
  stateKind: "loaded";
  blogPost: BLOG_POST;
}

type BlogPostState = UnloadedBlogPost | LoadedBlogPost;
这将允许您在博客文章加载之前显式表示UI的状态,如果您想添加微调器或其他加载指示器,这可能会很有用。您的还原程序仍然需要检查日志是否已加载,但如果日志未加载,则提前退出非常简单:

UPDATE_CATEGORY(state, action:UPDATE_CATEGORY) {
  // assuming state.blogPostState is of type BlogPostState...
  if (state.blogPostState.stateKind === "unloaded") {
    return state;
  }

  // state.blogPostState is narrowed to type LoadedBlogPost, state.blogPostState.blogPost can be accessed
},

作为补充说明,在使用Redux时,您通常希望简化程序是纯函数,返回新的状态对象,而不是改变现有状态。请参阅。

问题在于类型脚本是正确的。在应用程序中,有时状态将为
null

如果在API调用完成之前尝试访问状态,会发生什么?Typescript保护您不受此影响

您需要对此进行大量检查,但这可能是添加加载器的好机会

type BLOGPOST = {name: string};
type ADMIN_BLOGPOST_STATE = {
  blogPost: null | BLOGPOST
}
const state:ADMIN_BLOGPOST_STATE = {
  blogPost: null
}

// .. api calls eventually set state.blogPost to some valid value

if (state.blogPost === null) {
   return <Loader/>
} else {
   return <BlogPost id={state.blogPost.id} title={state.blogPost.title}/>
}
这样做的好处是,一开始就将断言放在一个地方


不利的一面是,您需要特别小心如何访问您的状态。你只能靠自己。

我没有选择正确的答案,因为它们都帮助我了解了这一点

不管怎样,我最后做的是:

type ADMIN_BLOGPOST_STATE = {
  blogPost: BLOGPOST
}
  • 我已经用一个
    加载
    布尔值来控制UI状态。因此,在数据到达之前,用户看到的唯一界面是
我决定去掉
null
值,并为
blogPost
添加了一个空白的valid值作为初始状态。我所说的blank是指它没有任何内容,但是它拥有所有需要的属性,并且它完全实现了
BLOGPOST
类型

然后,我的类型变成如下:

type ADMIN_BLOGPOST_STATE = {
  blogPost: BLOGPOST
}
这不仅帮助我摆脱了reducer中的类型断言,而且还允许我
type ADMIN_BLOGPOST_STATE = {
  blogPost: BLOGPOST
}
export function useAdminBlogPostProperty<K extends keyof TYPES.BLOGPOST>(propName: K): TYPES.BLOGPOST[K] {
  return useSelector((state: ROOT_STATE) => state.ADMIN_BLOGPOST.blogPost[propName]);
}
type ADMIN_BLOGPOST_STATE = {
  blogPost: BLOGPOST
}
// THIS FUNCTION ALLOWS TO PASS NULL AND STILL OBEY
// THE STATE CONTRACT. BECAUSE WE KNOW FOR SURE THAT
// BECAUSE INITIAL STATE WILL NEVER BE ACCESSED

export const initialNullState = <T extends unknown>() : T => {
  return null as T;
};
const getInitialState = (): PAGES.ADMIN_BLOGPOST => ({
  status: { loading: true, error: null, notFound: false },
  blogPost: initialNullState(),
});