当试图实现函数(泛型)的联合类型时, typescript错误



typescript playground

所以我试图创建一个更通用的函数,它可以帮助我从一个对象转换数据,而不是其他东西。

然而,在我目前的实现中,我得到了typescript错误

Element implicitly has an 'any' type because expression of type '"channel" | "id" | "name"' can't be used to index type '{ id: string; name: string; source: string; }'.
Property 'channel' does not exist on type '{ id: string; name: string; source: string; }'.

由于键可能不匹配,但我不确定如何使其成为一个适当的泛型函数。

代码:

type channelCreateChanges = {
change_from : {}
change_to : {
id : string,
name : string,
source : string,
},
datetime : string,
operation : "create",
entity : "channel",
}
type channelUpdateChanges = {
change_from : {
id : string,
name : string,
source : string,
}
change_to : {
id : string,
name : string,
source : string,
},
datetime : string,
operation : "update",
entity : "channel",
}
type programCreateChanges = {
change_from : {
}
change_to : {
channel : string,
id : string,
name : string,
origin : string,
},
datetime : string,
operation : "create",
entity : "program",
}
type programDeleteChanges = {
change_from : {
channel : string,
id : string,
name : string,
origin : string,
}
change_to : {
},
datetime : string,
operation : "delete",
entity : "program",
}
type dataChangeRow = {
from : string,
to : string,
datetime : string,
operation : string,
entity : string
}
const programKeys = [
"id",
"channel"
] as const;
const channelKeys = [
"id",
"name",
] as const;
type allChanges = channelCreateChanges | channelUpdateChanges | programCreateChanges | programDeleteChanges
type allKeys = typeof programKeys | typeof channelKeys
function ANY_CHANGE_LOG_TO_ROWS (data : allChanges, keys : allKeys) : dataChangeRow[] {
const rows = [] as dataChangeRow[];
for (const key of keys) {
const row = {
datetime : data.datetime,
entity : data.entity,
operation : data.operation
} as dataChangeRow;
if (data.operation === "create") {
row.from = `-`;
row.to = `${key}: ${data.change_to[key]}`;
}
else if (data.operation === "update") {
row.from = `${key}: ${data.change_from[key]}`;
row.to = `${key}: ${data.change_to[key]}`;
}
else if (data.operation === "delete") {
row.from = `${key}: ${data.change_from[key]}`;
row.to = `-`;
}
rows.push(row);
}
return rows;
}

正如Robby注意到的那样,要使此工作,您需要Channel具有' Channel '属性。

我建议忘记为每个对象类型的特定日志指定键。(可能不是你想要的)

这是一个真正的泛型实现:

type Channel = { id : string; name : string; source : string }
type Program = { id : string; name : string; origin : string; channel : string }
type OperationType = "create" | "update" | "delete";
type Operation<ENTITY, OP extends OperationType> = {
change_from : OP extends "update" | "delete" ?  ENTITY : undefined;
change_to : OP extends "update" | "create" ? ENTITY : undefined;
datetime : string,
operation : OP,
entity : ENTITY extends Channel ? 'Channel' : ENTITY extends  Program ? 'Program' : 'unknown';
} 
type channelCreateChanges = Operation<Channel,"create">
type channelUpdateChanges = Operation<Channel,"update">
type programCreateChanges = Operation<Program,"create">
type programDeleteChanges = Operation<Program,"delete">

type dataChangeRow = {
from : string,
to : string,
datetime : string,
operation : string,
entity : string
}
type allChanges = channelCreateChanges | channelUpdateChanges | programCreateChanges | programDeleteChanges
function ANY_CHANGE_LOG_TO_ROWS (allOperations : Array<allChanges>) : dataChangeRow[] {
const rows = allOperations.map((ope) => {
const row : dataChangeRow = {
from : ope.change_from ? JSON.stringify(ope.change_from) : '-',
to : ope.change_to ? JSON.stringify(ope.change_to) : '-',
datetime : ope.datetime,
entity : ope.entity,
operation : ope.operation
} ;
return row;
})

return rows;
}

如果你真的需要记录特定的键,请注意你可以得到这些:

type AllKeys = keyof Channel | keyof Program; // "id" | "name" | "source" | "origin" | "channel"
type CommonKeys = keyof Channel & keyof Program; // "id" | "name" 

你可以看到你使用的是Allkeys,但你真正想要的是CommonKey,通过索引来处理任何频道或程序。

如果您需要'channel'属性,您必须管理使CommonKeys等于"id"|"name"|"channel">

例如,您可以在通道类型中添加{channel:undefined}属性。

OK !

现在我明白你的意思了,对不起。

我让你在下面的链接中看到细节和测试,但总的来说,这里是你的函数实现:

const ANY_CHANGE_LOG_TO_ROWS = <ENTITY, OP extends OperationType, K extends keyof ENTITY>(ope : Operation<ENTITY,OP>, keyToSerialise : Array<AllKeys>) : dataChangeRow[] => {
{
const rows = Array<dataChangeRow>();

const entityKeys = (ope.change_to === undefined ? Object.keys(ope.change_from!) : Object.keys(ope.change_to!) )  as Array<K>;
for(const key of entityKeys)
if(keyToSerialise.includes(key as AllKeys)) {
const row : dataChangeRow = {
from : ope.change_from ? (key as string + ":" + (ope.change_from as ENTITY)[key]): '-',
to : ope.change_to ?  (key as string + ":" + (ope.change_to as ENTITY)[key]): '-',
datetime : ope.datetime,
operation : ope.operation,
entity : ope.entity,
} ;
rows.push(row)
}
return rows;
}
}

有很多cast,我猜是有效的

请参阅此处查看更详细的代码,并对棘手的部分进行注释。

相关内容