gpt4 book ai didi

typescript - 如何在 typescript 中选择性地从一个 Partial 分配到另一个 Partial

转载 作者:行者123 更新时间:2023-12-04 14:56:35 27 4
gpt4 key购买 nike

在下面的 TypeScript 代码片段中,我需要从一个对象分配给另一个对象,其中它们都是 Partial<InjectMap> .在这里,我的直觉是 typescript 应该能够理解正在发生的事情,因为在第 (B) 行,key 的类型是 typeof InjectMap .因此,它应该能够从 input 分配值。至 output正确。


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
}
Playground Link
但它在 (C) 行给出了以下错误:
Type '"B" | "D" | undefined' is not assignable to type 'undefined'.
Type '"B"' is not assignable to type 'undefined'.
奇怪的是,如果我注释行 (A),错误就会消失。这是 TypeScript 的一个缺点还是我遗漏了什么?

最佳答案

我不认为这是一个错误,改变值几乎总是不安全的,而 TS 只是试图使其安全。
让我们从InjectMap开始界面。
很明显,您不能拥有非法状态,例如:

const illegal: InjectMap = {
"A": "D", // expected B
"C": "B" // expected D
}
这个很重要。
让我们继续我们的循环:
interface InjectMap {
"A": "B",
"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];

const inp = input[key] // "B" | "D" | undefined
const out = output[key] // "B" | "D" | undefined

output[key] = input[key]

}
因为 key是动态的,TS 不确定是否 B , Dundefined .我希望你同意我的看法,在这个地方正确的类型 inp"B" | "D" | undefined ,这是预期的行为,因为类型系统是静态的。
从, inputoutput不受 key 的约束, TS 想避免非法状态。为了清楚起见,请考虑下一个示例,它等于我们的
type KeyType_ = "B" | "D" | undefined

let keyB: KeyType_ = 'B';
let keyD: KeyType_ = 'D'

output[keyB] = input[keyD] // Boom, illegal state! Runtime error!
您可能已经注意到, keyBkeyD具有相同的类型但不同的值。
与您在示例中遇到的情况相同,TS 无法计算出它只能计算出类型的值。
如果你想让 TS 开心,你应该添加条件语句或 typeguard:
for (let i = 0; i < keys.length; i++) {
const key = keys[i];

if (key === 'A') {
let out = output[key] // "B"
let inp = input[key] // "B"

output[key] = input[key] // ok
}

if (key === 'C') {
let out = output[key] // "D"
let inp = input[key] // "D"

output[key] = input[key] // ok
}
}
请记住,当你改变你的值时,你失去了类型保证。
thisthis关于突变的问题。
还有这个 talk提香·德拉戈米尔·切尔尼科娃 (Titian Dragomir Cernicova) 的作品相当不错。
这里有一个不安全突变的例子,摘自@Titian 的演讲:
type Type = {
name: string
}

type SubTypeA = Type & {
salary: string
}

type SubTypeB = Type & {
car: boolean
}

type Extends<T, U> =
T extends U ? true : false


let employee: SubTypeA = {
name: 'John Doe',
salary: '1000$'
}

let human: Type = {
name: 'Morgan Freeman'
}

let director: SubTypeB = {
name: 'Will',
car: true
}


// same direction
type Covariance<T> = {
box: T
}

let employeeInBox: Covariance<SubTypeA> = {
box: employee
}

let humanInBox: Covariance<Type> = {
box: human
}

// Mutation ob object property
let test: Covariance<Type> = employeeInBox

test.box = director // mutation of employeeInBox

const result_ = employeeInBox.box.salary // while result_ is undefined, it is infered a a string


// Mutation of Array
let array: Array<Type> = []
let employees = [employee]
array = employees
array.push(director)

const result = employees.map(elem => elem.salary) // while salary is [string, undefined], is is infered as a string[]

console.log({result_,result})
Playground
如何解决?
请让我知道它是否适合您:

export interface InjectMap {
"A": "B",
"C": "D"
}

const assign = <Input extends InjectMap, Output extends InjectMap>(
input: Partial<Input>,
output: Partial<Output>,
keys: Array<keyof InjectMap>
) => keys.reduce((acc, elem) => ({
...acc,
[elem]: input[elem]
}), output)
Playground
更新

why does this analysis apply for [elem]: input[elem]? input[elem] can again be "B"|"D"|undefined and thus the compiler should give error again. But, here compiler is intelligent to know that the type of input[elem] applies for [elem]. What is the difference?


这是一个很好的问题。
当您使用计算键创建新对象时,TS 使此对象 indexed by string ,我的意思是,您可以使用任何字符串 prop你要
const computedProperty = (prop: keyof InjectMap) => {
const result = {
[prop]: 'some prop' // { [x: string]: string; }
}

return result
}
它给你更多的自由,但也提供了一些不安全。

With great power comes great responsibility


因为现在,不幸的是,您可以这样做:
const assign = <Input extends InjectMap, Output extends InjectMap>(
input: Partial<Input>,
output: Partial<Output>,
keys: Array<keyof InjectMap>
) => keys.reduce((acc, elem) => {

return {
...acc,
[elem]: 1 // unsafe behavior
}

}, output)
您可能已经注意到,返回类型为 assign功能是 Partial<Output> ,这是不正确的。
因此,为了使其完全类型安全,您可以使用 typeguards ,但我认为这会过于复杂

关于typescript - 如何在 typescript 中选择性地从一个 Partial 分配到另一个 Partial,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67857960/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com