Type narrowing with control-flow analysis for bracket element access has been improved with TypeScript 4.7.
What I additionally would like to do is check whether the accessed field is an array or not. Right now, I don't know why the type-guard does not work:
Error: Type 'never[]' is not assignable to type 'string'.(2322)
As you can see in the placeholder comment, TypeScript still thinks that the accessed field could be of type string, even though isArray should've made clear that the field is some kind of array.
What am I missing?
Here is a complete example:
Imagine we have a Post type, where some of the fields are arrays:
type Post = {
id: string;
title: string;
chapters: PostChapter[];
};
Now, I have list of strings (keys of a Post), which I want to use to overwrite the value in a Post object dynamically:
const fieldNamesToReplace: (keyof Post)[] = ["chapters"];
When using that keys with bracketed access, TypeScript doesn't know that it is an array, even when you check for it via Array.isArray.
Btw: What does work (as a workaround?) is just creating a new object and overwriting the field, as we don't rely on the control-analysis for bracketed access.
Here is a playground link and the full example:
type PostChapter = {
id: string;
chapterTitle: string;
};
type Post = {
id: string;
title: string;
chapters: PostChapter[];
};
const fieldNamesToReplace: (keyof Post)[] = ["chapters"];
const posts: Post[] = [
{
id: "1",
title: "abc",
chapters: [{ id: "1.1", chapterTitle: "def" }],
},
];
const postsTransformed = posts.map((post) => {
let postNew = { ...post };
// works, because we don't rely on the type-narrowing for setting the value
fieldNamesToReplace.forEach((fieldName) => {
if (Array.isArray(postNew[fieldName])) {
postNew = { ...postNew, [fieldName]: [] };
}
});
// doesn't work
fieldNamesToReplace.forEach((fieldName) => {
if (Array.isArray(postNew[fieldName])) {
postNew[fieldName] = [];
// Error: Type 'never[]' is not assignable to type 'string'.(2322)
const placeholder = postNew[fieldName];
// ^?
}
});
return postNew;
});
