Typescript 为什么此映射类型要删除“?”装饰符?我们如何才能在不删除它的情况下实现类似的结果? 问题

Typescript 为什么此映射类型要删除“?”装饰符?我们如何才能在不删除它的情况下实现类似的结果? 问题,typescript,types,mapped-types,Typescript,Types,Mapped Types,我们正在构建一个映射类型,该类型不包括函数类型的属性。我们的方法有一个问题:它还从映射属性中删除可选(?)装饰器 繁殖 下面是这种行为的一个例子NoOpMap1的行为符合我们的要求,NoOpMap2的行为有问题 键入NoOpMap1={//Good。这一个不会删除? [K in keyof T]:T[K]; }; 类型键={ [K in keyof T]:K; }[keyof T]; 键入NoOpMap2={//Problem。此操作将删除? [K in键]:T[K]; }; 演示 type

我们正在构建一个映射类型,该类型不包括
函数
类型的属性。我们的方法有一个问题:它还从映射属性中删除可选(
)装饰器

繁殖 下面是这种行为的一个例子
NoOpMap1
的行为符合我们的要求,
NoOpMap2
的行为有问题

键入NoOpMap1={//Good。这一个不会删除?
[K in keyof T]:T[K];
};
类型键={
[K in keyof T]:K;
}[keyof T];
键入NoOpMap2={//Problem。此操作将删除?
[K in键]:T[K];
};
演示

type SomeType={
foo?:字符串,
}
//键入SomeTypeNoOpMap1={foo?:string;}
键入SomeTypeNoOpMap1=NoOpMap1;
//键入SomeTypeNoOpMap2={foo:string;}
键入SomeTypeNoOpMap2=NoOpMap2;
NoOpMap1
的行为符合预期。它将
装饰器保留在
foo
属性上<代码>NoOpMap2将其删除

问题: 为什么
NoOpMap2
正在删除
装饰器?我们如何才能在不删除它的情况下实现类似的结果

实际用例 以下是我们尝试构建的完整类型:

类型DataPropertyNames={
[K in keyof T]:T[K]扩展函数?never:K;
}[keyof T];
类型DataPropertiesOnly={
[DataPropertyNames中的K]
:T[K]扩展(字符串|数字|布尔值)?T[K]
:T[K]扩展(推断A)[]?DataPropertiesOnly[]
:仅限DataProperties;
};

如上所述,上述类型负责删除
Function
类型的属性,而无需从其余属性中删除
装饰器。

如果要在中保留属性的可选/只读状态,则需要确保编译器将映射视为。我知道两种方法

一种是映射形式为
{[K in keyof T]:…}
,其中您直接映射一些
T
,通用或具体的
keyof T
。您必须在类型中直接显示类似于
in-keyof
的内容,否则它将不起作用

interface Foo {
    optional?: string;
    readonly viewonly: string;
}

type Homomorphic = { [K in keyof Foo]: 0 };
// type Homomorphic = { 
//   optional?: 0 | undefined; 
//   readonly viewonly: 0; 
// }

type KeyOf<T> = keyof T
type NonHomomorphic = { [K in KeyOf<Foo>]: 0 };
// type NonHomomorphic = { 
//   optional: 0; 
//   viewonly: 0; 
// }
后一种方法是将部分映射类型(如
Pick
)设置为同态。正是这种方法让您的实际用例发挥作用:

// unchanged
type DataPropertyNames<T> = {
    [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];

// quick abort if T is a function or primitive
// otherwise pass to a homomorphic helper type 
type DataPropertiesOnly<T> =
    T extends Function ? never :
    T extends object ? DPO<T, DataPropertyNames<T>> :
    T

// homomorphic helper type
type DPO<T, KT extends keyof T> = {
    [K in KT]
    : T[K] extends (string | number | boolean) ? T[K]
    : T[K] extends (infer A)[] ? DataPropertiesOnly<A>[]
    : DataPropertiesOnly<T[K]>;
}
//未更改
类型DataPropertyNames={
[K in keyof T]:T[K]扩展函数?never:K;
}[keyof T];
//如果T是函数或原语,则快速中止
//否则传递给同态助手类型
仅键入DataProperties=
T扩展函数?从未:
T扩展对象?DPO:
T
//同态辅助型
DPO类型={
[K单位:KT]
:T[K]扩展(字符串|数字|布尔值)?T[K]
:T[K]扩展(推断A)[]?DataPropertiesOnly[]
:仅限DataProperties;
}

我想那会按照你的意愿行事。好吧,希望这会有帮助;祝你好运

你学习过范畴理论吗?没有正式学习;我希望我在这方面有更扎实的背景,因为事实证明这和计算机编程语言类型系统中发生的许多事情有关。
// unchanged
type DataPropertyNames<T> = {
    [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];

// quick abort if T is a function or primitive
// otherwise pass to a homomorphic helper type 
type DataPropertiesOnly<T> =
    T extends Function ? never :
    T extends object ? DPO<T, DataPropertyNames<T>> :
    T

// homomorphic helper type
type DPO<T, KT extends keyof T> = {
    [K in KT]
    : T[K] extends (string | number | boolean) ? T[K]
    : T[K] extends (infer A)[] ? DataPropertiesOnly<A>[]
    : DataPropertiesOnly<T[K]>;
}