- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试将我的 Angular 2 应用程序迁移到 Angular 4 和 ngrx 4。
我正在解决一个奇怪的 typescript 编译问题,这是我在这次更新之前没有遇到过的。我在离线环境中工作,所以我不能在这里分享确切的代码。
我在网上找了这个问题,我能找到的最接近的是这个问题: question about payload types这个问题中的代码与我试图做的有点相似,但不同之处在于我从操作中删除了 ActionTypes 对象(这是该问题的建议答案)但它没有解决我的编译错误。
我不能把我处理的确切代码放在这里,但我的代码完全基于 ngrx 示例 ngrx official example on github
让我们以 book reducer 和 book actions 为例 book actions book reducer
我的代码基本上是一样的,当我试图从 action 中获取 payload 时,每个 reducer case(在 switch case 中)都会出现错误,它告诉我类似这样的事情:
Type 'string | Book | Book[]' is not assignable to type 'Book'. Type 'string' is not assignable to type 'Book'.
(在我的代码中,我有不同的类而不是 Book,但它的想法几乎相同)
现在我正在通过转换 A | 中的 Action 类型来解决错误乙 |乙成为与开关中的案例对应的特定操作类型(使用 as typescript 关键字)。这个解决方案感觉很糟糕而且很hacky,我想知道是否有更好的解决方案。
我使用的版本: typescript :2.4.1(我怀疑它可能连接到未使用与我相同的更新 typescript 版本的 ngrx 示例)ngrx@商店:4.0.3ngrx@核心:1.2.0angular:(最新的 17.9.17,angular 中有很多包,我认为我使用哪个 angular 来解决这个错误并不重要,但我说这只是为了安全起见) typescript :(2.4.1)(我阅读了ngrx官方迁移文档关于需要升级 typescript ,所以我做了)
更新:由于我得到的答案与我的问题无关,而且我也被问过,所以这是我的代码失败的部分(我无法发布确切的帖子,但我手动复制了相关部分):
mission-reducer.ts:
import * as MissionActionsFile from "./mission-actions";
export type State = .... // doesnt matter
const initialState: State = // doesnt matter as well
export function reducer(state: State = initialState, action:
MissionActionsFile.Actions): State {
switch(action.type) {
case MissionActionsFile.ADD_SUCCESS:
{
const mission: Mission = action.payload; // This line generates the
// following error:
// Type 'string | number | Mission | MissionRealTime | ...' is not
// assignable
// to type 'Mission'. Type 'string' is not assignable to type 'Mission'
return {}; // This doesnt matter too
}
// There are more cases and a deafult one, but that doesn't matter
}
}
任务 Action .ts:
// I will only give the actual action defenition for AddSuccess and for AddFailed since we are using AddSuccess in the example and
// addFailed is an example where the payload type is string, which according to the error is the problem, though
// there are more action types and other payload types with other classes as the quoted error implies.
export const ADD_SUCCESS: string = "[Mission] AddSuccess";
export const ADD_FAILED: string = "[Mission] AddFailed";
export class AddSuccessAction implements Action {
public readonly type: string = ADD_SUCCESS;
constructor(public payload: Mission) {}
}
export class AddFailedAction implements Action {
public readonly type:string = ADD_FAILED;
constructor(public payload: string) {}
}
// More actions are defined here but im not gonna copy all
...
export type Actions =
AddSuccessAction |
AddFailedAction;
总而言之,我明白为什么 typescript 认为 Action 负载类型可能是 string |使命 | ...但是在我基于此的 ngrx 示例中,似乎 typescript 知道在那里可以推断出这种情况下的特定类型,但对我来说,由于我不理解的原因,这不起作用,可能与我使用 typescript 2.4 有关.1?不确定,需要这方面的帮助
最佳答案
问题是代码的行为和它的类型注释是相互交叉的。
实际上,我会说代码注释过多。
联合类型和它们支持的案例分析通过类型推断和基于控制流的类型分析来工作,这是 TypeScript 最强大的两个功能。语言从联合中消除可能性的过程称为缩窄。
正如代码所建议的,通过对称为判别式的属性执行值测试,可以将联合类型分解为其组成部分。
判别式是一种属性,其类型具有有限 可能值集,每个值通常对应于并集的一种情况。
类型 string
不是有效的判别式,但是类型 "hello world"
是因为,作为所有可能字符串的父类(super class)型,字符串类型的联合包括string
折叠为 string
。例如,类型 string | “hello world”
正是 string
类型。
当我们在 TypeScript 中定义 const
或 readonly
属性时,编译器会将其类型推断为初始化文字的类型。
考虑:
const kind = "first";
这里kind
的类型不是string
而是"first"
。
同样,给定
class Kindred {
readonly kind = "first";
}
kind
属性的类型不是string
,而是"first"
。
在我们的代码中,判别式是一个名为 type
的属性,由并集的每个组成部分定义。
但是,虽然您已正确地为每个成员提供了唯一值,但您使用类型注释过度指定了它们以防止缩小。
你有什么:
export class AddSuccessAction {
public readonly type: string = ADD_SUCCESS;
constructor(public payload: Mission) {}
}
export class AddFailedAction {
public readonly type: string = ADD_FAILED;
constructor(public payload: string) {}
}
你想要什么:
export class AddSuccessAction {
readonly type = ADD_SUCCESS;
constructor(public payload: Mission) {}
}
export class AddFailedAction {
readonly type = ADD_FAILED;
constructor(public payload: string) {}
}
一个工作的开关
switch (action.type) {
// action.type is `"[Mission] AddSuccess" | "[Mission] AddFailed"`
// action.payload is `string | Mission`
case missionActions.ADD_SUCCESS:
// action.type is `"[Mission] AddSuccess"`
// action.payload is `Mission`
const mission = action.payload;
}
为什么这很重要:
字符串文字类型是一种常见且惯用的方式来区分联合的可能性,但是,通过将实现属性声明为 string
类型,该类型是所有字符串文字的父类(super class)型类型,我们抑制了类型推断,从而防止了缩小。请注意,string
不是可以缩小范围的类型。
一般来说,当一个值有一个初始化器时,最好利用 TypeScript 的类型推断。除了实现预期的场景之外,您还会对类型推断器捕获的错误数量印象深刻。我们不应该告诉编译器比它需要知道的更多,除非我们有意想要一个比它推断的更通用的类型。使用 --noImplicitAny
,它总是让我们知道何时需要以类型注释的形式指定额外信息。
备注:
您可以通过将文字值指定为 const
或 readonly
属性的类型,从技术上指定类型并仍然保留缩小行为。然而,这增加了维护成本并且相当多余。
例如,以下是有效的:
export const ADD_FAILED: "[Mission] AddFailed" = "[Mission] AddFailed";
但你只是在不必要地重复自己。
在他们的回答中,ilyabasiuk 提供了一些很好的引用链接,介绍了文字类型的使用,特别是在 ngrx 中,以及它如何随着 TypeScript 语言的最近迭代而演变。
要理解为什么在不可变位置推断文字类型,请考虑它支持强大的静态分析,从而更好地检测错误。
考虑:
type Direction = "N" | "E" | "S" | "W";
declare function getDirection(): Direction;
const currentDirection = getDirection();
if (currentDirection === "N") { // currentDirection is "N" | "E" | "S" | "W"
}
// Error: this is impossible, unreachable code as currentDirection is "E" | "S" | "W"
// the compiler knows and will give us an error here.
else if (currentDirection === "N") {
}
关于angular - reducer Action 中的 ngrx 有效负载未编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46264002/
我想做的是让 JTextPane 在 JPanel 中占用尽可能多的空间。对于我使用的 UpdateInfoPanel: public class UpdateInfoPanel extends JP
我在 JPanel 中有一个 JTextArea,我想将其与 JScrollPane 一起使用。我正在使用 GridBagLayout。当我运行它时,框架似乎为 JScrollPane 腾出了空间,但
我想在 xcode 中实现以下功能。 我有一个 View Controller 。在这个 UIViewController 中,我有一个 UITabBar。它们下面是一个 UIView。将 UITab
有谁知道Firebird 2.5有没有类似于SQL中“STUFF”函数的功能? 我有一个包含父用户记录的表,另一个表包含与父相关的子用户记录。我希望能够提取用户拥有的“ROLES”的逗号分隔字符串,而
我想使用 JSON 作为 mirth channel 的输入和输出,例如详细信息保存在数据库中或创建 HL7 消息。 简而言之,输入为 JSON 解析它并输出为任何格式。 最佳答案 var objec
通常我会使用 R 并执行 merge.by,但这个文件似乎太大了,部门中的任何一台计算机都无法处理它! (任何从事遗传学工作的人的附加信息)本质上,插补似乎删除了 snp ID 的 rs 数字,我只剩
我有一个以前可能被问过的问题,但我很难找到正确的描述。我希望有人能帮助我。 在下面的代码中,我设置了varprice,我想添加javascript变量accu_id以通过rails在我的数据库中查找记
我有一个简单的 SVG 文件,在 Firefox 中可以正常查看 - 它的一些包装文本使用 foreignObject 包含一些 HTML - 文本包装在 div 中:
所以我正在为学校编写一个 Ruby 程序,如果某个值是 1 或 3,则将 bool 值更改为 true,如果是 0 或 2,则更改为 false。由于我有 Java 背景,所以我认为这段代码应该有效:
我做了什么: 我在这些账户之间创建了 VPC 对等连接 互联网网关也连接到每个 VPC 还配置了路由表(以允许来自双方的流量) 情况1: 当这两个 VPC 在同一个账户中时,我成功测试了从另一个 La
我有一个名为 contacts 的表: user_id contact_id 10294 10295 10294 10293 10293 10294 102
我正在使用 Magento 中的新模板。为避免重复代码,我想为每个产品预览使用相同的子模板。 特别是我做了这样一个展示: $products = Mage::getModel('catalog/pro
“for”是否总是检查协议(protocol)中定义的每个函数中第一个参数的类型? 编辑(改写): 当协议(protocol)方法只有一个参数时,根据该单个参数的类型(直接或任意)找到实现。当协议(p
我想从我的 PHP 代码中调用 JavaScript 函数。我通过使用以下方法实现了这一点: echo ' drawChart($id); '; 这工作正常,但我想从我的 PHP 代码中获取数据,我使
这个问题已经有答案了: Event binding on dynamically created elements? (23 个回答) 已关闭 5 年前。 我有一个动态表单,我想在其中附加一些其他 h
我正在尝试找到一种解决方案,以在 componentDidMount 中的映射项上使用 setState。 我正在使用 GraphQL连同 Gatsby返回许多 data 项目,但要求在特定的 pat
我在 ScrollView 中有一个 View 。只要用户按住该 View ,我想每 80 毫秒调用一次方法。这是我已经实现的: final Runnable vibrate = new Runnab
我用 jni 开发了一个 android 应用程序。我在 GetStringUTFChars 的 dvmDecodeIndirectRef 中得到了一个 dvmabort。我只中止了一次。 为什么会这
当我到达我的 Activity 时,我调用 FragmentPagerAdapter 来处理我的不同选项卡。在我的一个选项卡中,我想显示一个 RecyclerView,但他从未出现过,有了断点,我看到
当我按下 Activity 中的按钮时,会弹出一个 DialogFragment。在对话框 fragment 中,有一个看起来像普通 ListView 的 RecyclerView。 我想要的行为是当
我是一名优秀的程序员,十分优秀!