如何在typescript中有选择地从一个分部分配到另一个分部

如何在typescript中有选择地从一个分部分配到另一个分部,typescript,Typescript,在下面的TypeScript代码片段中,我需要从一个对象分配到另一个对象,其中两个对象都是Partial。这里,我的直觉告诉我typescript应该能够理解发生了什么,因为在第(B)行,key的类型是typeof InjectMap。因此,它应该能够将输入中的值正确分配给输出 export interface InjectMap { "A": "B", // line (A) "

在下面的TypeScript代码片段中,我需要从一个对象分配到另一个对象,其中两个对象都是
Partial
。这里,我的直觉告诉我typescript应该能够理解发生了什么,因为在第(B)行,
key
的类型是
typeof InjectMap
。因此,它应该能够将
输入
中的值正确分配给
输出


export interface InjectMap {
    "A": "B",                          // line (A)
    "C": "D"
}
type InjectKey = keyof InjectMap;

const input: Partial<InjectMap> = {};
const output: Partial<InjectMap> = {};
const keys: InjectKey[] = []

for (let i = 0; i < keys.length; i++) {
    const key = keys[i]                // line (B)
    output[key] = input[key]           // line (C)  - Gives Error
}

奇怪的是,如果我注释第(A)行,错误就会消失。这是TypeScript中的一个缺点还是我遗漏了什么?

我不认为这是一个bug,变异值几乎总是不安全的,TS只是试图使其安全

让我们从
InjectMap
接口开始

很明显,您不能有以下非法状态:

const非法:InjectMap={
“A”:“D”,//应为B
“C”:“B”//D
}
这很重要

让我们继续循环:

接口映射{
“A”:“B”,
“C”:“D”
}
类型InjectKey=InjectMap的keyof;
常量输入:部分={};
常量输出:部分={};
常量键:InjectKey[]=[]
for(设i=0;i
由于
是动态的,TS不确定它是
B
D
还是
未定义
。我希望您同意我的观点,在这里,
inp
的正确类型是
“B”|“D”|未定义的
,这是预期的行为,因为类型系统是静态的

由于,
输入
输出
未被
绑定,TS希望避免非法状态。为了说明清楚,请考虑下一个例子,它等于我们的

type KeyType=“B”|“D”|未定义
设keyB:KeyType_u2;='B';
让keyD:KeyType_uz='D'
输出[keyB]=输入[keyD]//爆炸,非法状态!运行时错误!
您可能已经注意到,
keyB
keyD
的类型相同,但值不同

与示例中的情况相同,TS无法计算出它只能计算出的值类型

如果要使TS满意,应添加条件语句或typeguard:

for(设i=0;i
请记住,当你改变你的值时,你会失去类型保证

请参阅并询问有关突变的信息

另外,提香·德拉戈米尔·塞尔尼科娃的这首歌也不错

这里有一个不安全突变的例子,摘自@Titian的演讲:

类型={
名称:string
}
类型a=类型&{
薪水:string
}
类型B=类型和{
汽车:布尔型
}
类型扩展=
你呢?对:错
让员工:子类型a={
姓名:“约翰·多伊”,
工资:1000美元
}
让人类:类型={
姓名:“摩根·弗里曼”
}
let director:b={
姓名:'威尔',
汽车:是的
}
//同方向
类型协方差={
方框:T
}
让员工收件箱:协方差={
方框:雇员
}
让humanInBox:协方差={
盒子:人
}
//对象属性的变异
let test:协方差=员工收件箱
test.box=主管//员工收件箱的变异
const result_uu=employeeInBox.box.salary//result_u未定义时,推断为字符串
//阵列突变
让数组:数组=[]
让雇员=[雇员]
数组=员工
array.push(控制器)
const result=employees.map(elem=>elem.salary)//当salary为[string,undefined]时,被推断为字符串[]
log({result,result})

如何修复它?

请让我知道它是否适合您:


导出接口映射{
“A”:“B”,
“C”:“D”
}
常量赋值=(
输入:部分,
产出:部分,
关键字:数组
)=>按键减少((acc,elem)=>({
…acc,
[elem]:输入[elem]
}),输出)

更新

为什么此分析适用于[elem]:输入[elem]?输入[elem]也可以是“B”|“D”|未定义的,因此编译器应该再次给出错误。但是,在这里,编译器很聪明,知道输入[elem]的类型适用于[elem]。有什么区别

这是一个很好的问题

当您使用计算键创建新对象时,TS使该对象按字符串索引,我的意思是,您可以使用任何字符串
prop

const computedProperty=(prop:keyof InjectMap)=>{
常数结果={
[prop]:'someprop'/{[x:string]:string;}
}
返回结果
}
它给你更多的自由,但也提供了一点不安全

权力大,责任大

因为现在,不幸的是,你可以这样做:

const assign=(
输入:部分,
产出:部分,
关键字:数组
)=>键。减少((acc,elem)=>{
返回{
…acc,
[elem]:1//不安全行为
}
},输出)
您可能已经注意到,
assign
函数的返回类型是
Partial
,这是不正确的


因此,为了使其完全类型安全,您可以与一起使用,但我认为这将使输出[key]=“B”
过于复杂,这也会产生一个错误-类型“B”不能分配给类型“undefined”。在我看来,它更像是一个bug,更像是一个与字符串文本相关的问题。你真的打算定义
B
D
吗?还是仅仅作为示例?如果是这样,用接口替换它们将解决问题problem@AlonYampolski这是一个非常好的观察!
Type '"B" | "D" | undefined' is not assignable to type 'undefined'.
  Type '"B"' is not assignable to type 'undefined'.