I have this code, that I have converted from plain js in Typescript:
type Paragraph = { paragraph: (data: { text: string }) => string };
type Header = { header: (data: { level: string; text: string }) => string };
type List = { list: (data: { style: string; items: string[] }) => string };
type Block = {
type: string;
data: string;
};
export default class HTMLEngine {
static get generators(): Paragraph & Header & List {
return {
paragraph: (data: { text: string }) => `<p>${data.text}</p>`,
header: (data: { level: string; text: string }) => `<h${data.level}>${data.text}</h${data.level}>`,
list: (data: { style: string; items: string[] }) => {
const tagname = `${data.style.charAt(0)}l`;
return `<${tagname}>${data.items.map(item => `<li>${item}</li>`)}</${tagname}>`;
}
};
}
render(ejsData: string): string {
try {
const parsed = JSON.parse(ejsData).blocks;
return parsed.reduce((output: string, block: Block) => output + this.renderBlock(block), '');
} catch (e) {
return ejsData || '';
}
}
renderBlock(block: Block): string {
return `${HTMLEngine.generators[block.type](block.data)}\n`;
}
}
It works perfectly but in strict mode I get this error on this line
return `${HTMLEngine.generators[block.type](block.data)}\n`;
(property) HTMLEngine.generators: Paragraph & Header & List
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Paragraph & Header & List'.
No index signature with a parameter of type 'string' was found on type 'Paragraph & Header & List'.ts(7053)
I have no clue on fixing this error. Could someone help?
I think you want Block
to be a discriminated union type where its type
property determines exactly what type the data
property will be. You can write that manually, or, if you want, generate this programmatically from your Paragraph
, Header
, and List
:
type AllBlocks = Paragraph & Header & List;
type Block = { [K in keyof AllBlocks]: { type: K, data: Parameters<AllBlocks[K]>[0] } }[keyof AllBlocks]
/* type Block = {
type: "paragraph";
data: {
text: string;
};
} | {
type: "header";
data: {
level: string;
text: string;
};
} | {
type: "list";
data: {
style: string;
items: string[];
};
} */
This doesn't exactly solve your problem though, due to TypeScript's lack of support for what I've been calling correlated record types (see Microsoft/TypeScript#30581 for more information). The compiler sees HTMLEngine.generators[block.type]
and block.data
as unrelated union types that might not be usable together, even though block.data
is always appropriate for the function you get with block.type
.
Currently you can work around this by writing redundant code to walk the compiler through the possibilities:
renderBlock(block: Block): string {
switch (block.type) {
case "header":
return `${HTMLEngine.generators[block.type](block.data)}\n`;
case "list":
return `${HTMLEngine.generators[block.type](block.data)}\n`;
case "paragraph":
return `${HTMLEngine.generators[block.type](block.data)}\n`;
}
}
or, (more reasonably), use a type assertion to tell the compiler not to worry about its inability to verify safety here:
renderBlock(block: Block): string {
const generator = HTMLEngine.generators[block.type] as (x: Parameters<AllBlocks[keyof AllBlocks]>[0]) => string;
return `${generator(block.data)}\n`;
}
or the simpler
renderBlock(block: Block): string {
return `${HTMLEngine.generators[block.type](block.data as any)}\n`;
}
Issue is that your Block
type isn't precise enough. So the type
of a Block
shouldn't be just a string
but 'paragraph' | 'header' | 'list'
'paragraph' | 'header' | 'list'
'paragraph' | 'header' | 'list'
to match the types Paragraph
, Header
and List
.
Same for the data
attribute, it should be { text: string } & { level: string; text: string } & { style: string; items: string[] }
{ text: string } & { level: string; text: string } & { style: string; items: string[] }
{ text: string } & { level: string; text: string } & { style: string; items: string[] }
.
So your Block
type will look like that:
type Block = {
type: 'paragraph' | 'header' | 'list';
data: { text: string } & { level: string; text: string } & { style: string; items: string[] };
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.