typescript类型函数-您能否修改此函数以进行严格的多余属性检查,以便它能够处理签名索引?

typescript类型函数-您能否修改此函数以进行严格的多余属性检查,以便它能够处理签名索引?,typescript,Typescript,在某些情况下,这允许在将文本对象初始值设定项指定给类型联合时进行严格的多余属性检查 注意:下面详细描述了在将文本对象初始值设定项赋值给联合时严格超额属性检查的定义 第二类是一个例外的一般规则的情况下,一个常数,即const声明。在这种情况下,不允许有过多的属性。在本次讨论的范围内,我们将这种严格的分配称为: 将文本对象初始值设定项赋值给类型的并集 在Typescript中,一个新的类型可以组成为“类型的并集”,符号是 type TU= T1|T2|T3 union type()的正式Type

在某些情况下,这允许在将文本对象初始值设定项指定给类型联合时进行严格的多余属性检查

注意:下面详细描述了在将文本对象初始值设定项赋值给联合时严格超额属性检查的定义

第二类是一个例外的一般规则的情况下,一个常数,即
const
声明。在这种情况下,不允许有过多的属性。在本次讨论的范围内,我们将这种严格的分配称为:

将文本对象初始值设定项赋值给类型的并集 在Typescript中,一个新的类型可以组成为“类型的并集”,符号是

type TU= T1|T2|T3 
union type()的正式Typescript定义如中所述:

在TypeScript 3.5中,类型检查器至少验证所有提供的属性是否属于某个联合成员并具有适当的类型,这意味着上面的示例正确地发出了错误。请注意,只要属性类型有效,仍允许部分重叠

也就是说,根据定义,Typescript中的Union是正确的定义,否则就不正确了

更受约束的用例-严格联合 假设一个程序员有一个用例,该用例要求定义一个与以下psuedo代码定义相似但不相同的Typescript联合体:

如果
. TU型=紧密连接(T1,…,Ti,…,TN)
然后
. 常数t:TU=x
当且仅当
. 常数t:Ti=x
是至少一个StrictUnion参数的有效严格赋值

严格联合的一个明显用例是不允许意外地混合来自不同对象类型的属性。因此,在本SE问题的范围内
. 将文字对象初始值设定项赋值给联合时严格检查多余属性
定义为同义词 . 严格联合 后者要短得多

(注:同一严格联合的另一个用例在下面的附录中讨论)

此外,优选但不要求
StrictUnion(T1,…,TN)
可以作为Typescript联合的函数编写,即
StrictUnion(T1 |..TN)
,(这
排除联合(..)

回到原来的问题。 本文中提出的问题是如何修改
ExclusifyUnion
,以便它处理具有索引签名属性的类型的情况,从而给出与上述严格union定义兼容的结果

该问题还人为地将所考虑的类型和对象初始值设定项的域限制为仅1级深度,而不是具有属性的函数的边缘情况。但是,它必须包括具有

应该注意的是,Typescript的联合确实扩展到了更深层次的对象和数组——例如

type sth = { value: number, data: string } | { value: number, note: string };
const a: sth = { value: 7, data: 'test' };
const b: sth = { value: 7, note: 'hello' };
const c: sth = { value: 7, data: 'test', note: 'hello' };
type xsth = ExclusifyUnion<sth>;
/* type xsth = {
    value: number;
    data: string;
    note?: undefined;
} | {
    value: number;
    note: string;
    data?: undefined;
} */
const z: xsth = { value: 7, data: 'test', note: 'hello' }; // error!
/* Type '{ value: number; data: string; note: string; }' is not assignable to
 type '{ value: number; data: string; note?: undefined; } | 
 { value: number; note: string; data?: undefined; }' */
type T1 = {a:number,b:number} 
type T2 = {a:number, c: (T1|T2)[]}
const t:T1|T2 = {a:1,c:[{a:1,b:2,c:[{a:1,b:2}]}]} // legal Typescript
是允许的。相应地,strict union必须扩展到覆盖相同的域,并且将拒绝该赋值,因为
t.c
既不能严格赋值给
T1
也不能严格赋值给
T2
。这个问题将是一个单独的问题,除非这个问题的答案恰好扩展到包括它

附件: 严格联合的另一个用例-JSONSchema兼容性

严格联合的另一个用例是定义可以在中实现的类型,而无需定义任何自定义关键字。下面讨论了定义自定义关键字的缺点:

在使用其他关键字扩展JSON模式标准时,您必须注意的问题是模式的可移植性和理解性。您必须在其他平台上支持这些关键字,并正确记录它们,以便每个人都能理解和使用您的模式


TL;博士,这是不可能的


我们想推出一个版本的
ExclusifyUnion
,它可以将一个常规的union
a | B
转换成另一种类型,这种类型的行为类似于
a | B
的“严格”版本,即使在
a
B
具有字符串索引签名的情况下也是如此

“严格”是指如果对象文字
{…}
不能直接分配给
a
(由于)类型的变量,如果不能直接分配给
B
类型的变量,那么它也不能直接分配给
ExclusifyUnion类型的变量,无法通过@CraigHicks表达
排除Union)

然后,您可以创建类似于
asStrictUnion()
的辅助函数,而不是使用类型注释:

type ExclusifyUnion<U, T extends U> = U extends any ?
  [T] extends [U] ? keyof T extends keyof U ? T : never : never : never;
const asStrictUnion = <U>() => <T extends U>(t: ExclusifyUnion<U, T>) => t;

type A = { a: 0 };
type B = { [k: string]: 1; }

const asStrictU = asStrictUnion<A | B>();

const goodUnionA = asStrictU({ a: 0 }); // okay ✔
const goodUnionB = asStrictU({ a: 1, c: 1 }); // okay ✔
const badUnionAB = asStrictU({ a: 0, c: 1 }); // error ✔
const badUnionAB1 = asStrictU({ a: 0, c: 0 }); // error ✔
我想这取决于你



您能解释一下您希望欧盟的
EU
是什么类型的吗?这将帮助我找出生成它的类型函数。例如,您可以做的最好的事情可能是在查看来自联盟其他成员的属性时忽略索引签名,例如。这使得
EU
变成
{value:string;data:string;}{[x:string]:number;value:number;data?:undefined}
。。。但这是一种奇怪的类型,我不确定它是否适合您想要的任何用例。@jcalz-我已经添加了我希望是对您的问题的一个深刻而完整的答案。不仅是“什么”,而且是“为什么”。对
StrictUnion
(what)和示例用例(why)有一个定义。在我考虑修改
ExclusifyUnion
以将
sthA | sthB
转换为正确的
EU
之前,我必须知道正确的
EU
具体是什么。您已经编写了一组判断该类型的标准,但没有具体说明该类型是什么。如果你知道,请具体告诉我
type T = { a:number, b:number };
const target:T= { a:1, b:2, c:3 }; // error: 
/* Type '{ a: number; b: number; c: number; }' is not assignable to type 'T'.
  Object literal may only specify known properties, 
and 'c' does not exist in type 'T'.(2322)*/
type TU= T1|T2|T3 
type T1 = {a:number,b:number} 
type T2 = {a:number, c: (T1|T2)[]}
const t:T1|T2 = {a:1,c:[{a:1,b:2,c:[{a:1,b:2}]}]} // legal Typescript
type A = { a: 0 };
type B = { [k: string]: 1; }
type U = A | B;

const goodA: A = { a: 0 }; // ✔
const goodUnionA: U = { a: 0 }; // ✔

const goodB: B = { a: 1, c: 1 }; // ✔
const goodUnionB: U = { a: 1, c: 1 }; // ✔

const badA: A = { a: 0, c: 1 } // c is excess ✔
const badB: B = { a: 0, c: 1 } // a is wrong type ✔
const badUnionAB: U = { a: 0, c: 1 }; // ❌

const badA1: A = { a: 0, c: 0 }; // c is excess ✔
const badB1: B = { a: 0, c: 0 }; // a and c are wrong type ✔
const badUnionAB1: U = { a: 0, c: 0 }; // c is wrong type  ✔
type StrictB = B;
type StrictA = { a: 0, b?: never, c?: never, d?: never, e?: never, /*...∞*/ };
type StrictA = { a: 0, [k: string]: undefined }; // error!
// Property 'a' of type '0' is not assignable to string index type 'undefined'
type StrictA = { a: 0, [k: string]: 0 };
type U = StrictA | StrictB;
const badUnionAB: U = { a: 0, c: 1 }; // 0 is not assignable to 1 ✔
const badUnionAB1: U = { a: 0, c: 0 }; // ❌
type ExclusifyUnion<U, T extends U> = U extends any ?
  [T] extends [U] ? keyof T extends keyof U ? T : never : never : never;
const asStrictUnion = <U>() => <T extends U>(t: ExclusifyUnion<U, T>) => t;

type A = { a: 0 };
type B = { [k: string]: 1; }

const asStrictU = asStrictUnion<A | B>();

const goodUnionA = asStrictU({ a: 0 }); // okay ✔
const goodUnionB = asStrictU({ a: 1, c: 1 }); // okay ✔
const badUnionAB = asStrictU({ a: 0, c: 1 }); // error ✔
const badUnionAB1 = asStrictU({ a: 0, c: 0 }); // error ✔
const z = { a: 0, z: 25 } as const; // no error
const a: A = z; // no error
asStrictU(a); // no error