Skip to content

Commit

Permalink
fix(language-core): property 'xyz' does not exist on type 'abc' when …
Browse files Browse the repository at this point in the history
…using v-for

close #4386
  • Loading branch information
johnsoncodehk committed May 16, 2024
1 parent 86e38db commit 94c3386
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 27 deletions.
4 changes: 0 additions & 4 deletions packages/language-core/lib/codegen/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,3 @@ export function* forEachElementNode(node: CompilerDOM.RootNode | CompilerDOM.Tem
}
}
}

export function isFragment(node: CompilerDOM.IfNode | CompilerDOM.ForNode) {
return node.codegenNode && 'consequent' in node.codegenNode && 'tag' in node.codegenNode.consequent && node.codegenNode.consequent.tag === CompilerDOM.FRAGMENT;
}
35 changes: 22 additions & 13 deletions packages/language-core/lib/codegen/template/vFor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { Code } from '../../types';
import { collectVars, createTsAst, endOfLine, newLine } from '../common';
import type { TemplateCodegenContext } from './context';
import type { TemplateCodegenOptions } from './index';
import { isFragment } from './index';
import { generateInterpolation } from './interpolation';
import { generateTemplateChild } from './templateChild';

Expand Down Expand Up @@ -51,28 +50,38 @@ export function* generateVFor(
for (const varName of forBlockVars) {
ctx.addLocalVariable(varName);
}
let isFragment = true;
for (const argument of node.codegenNode?.children.arguments ?? []) {
if (
argument.type === CompilerDOM.NodeTypes.JS_FUNCTION_EXPRESSION
&& argument.returns.type === CompilerDOM.NodeTypes.VNODE_CALL
&& argument.returns.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION
) {
if (argument.returns.tag !== CompilerDOM.FRAGMENT) {
isFragment = false;
continue;
}
for (const prop of argument.returns.props.properties) {
yield* generateInterpolation(
options,
ctx,
prop.value.loc.source,
prop.value.loc,
prop.value.loc.start.offset,
ctx.codeFeatures.all,
'(',
')',
);
yield endOfLine;
if (
prop.value.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& !prop.value.isStatic
) {
yield* generateInterpolation(
options,
ctx,
prop.value.content,
prop.value.loc,
prop.value.loc.start.offset,
ctx.codeFeatures.all,
'(',
')',
);
yield endOfLine;
}
}
}
}
if (isFragment(node)) {
if (isFragment) {
yield* ctx.resetDirectiveComments('end of v-for start');
}
let prev: CompilerDOM.TemplateChildNode | undefined;
Expand Down
8 changes: 7 additions & 1 deletion packages/language-core/lib/codegen/template/vIf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { Code } from '../../types';
import { newLine } from '../common';
import type { TemplateCodegenContext } from './context';
import type { TemplateCodegenOptions } from './index';
import { isFragment } from './index';
import { generateInterpolation } from './interpolation';
import { generateTemplateChild } from './templateChild';

Expand Down Expand Up @@ -74,3 +73,10 @@ export function* generateVIf(

ctx.blockConditions.length = originalBlockConditionsLength;
}

function isFragment(node: CompilerDOM.IfNode) {
return node.codegenNode
&& 'consequent' in node.codegenNode
&& 'tag' in node.codegenNode.consequent
&& node.codegenNode.consequent.tag === CompilerDOM.FRAGMENT;
}
25 changes: 16 additions & 9 deletions packages/tsc/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { fork } from 'child_process';

const binPath = require.resolve('../bin/vue-tsc.js');
const workspace = path.resolve(__dirname, '../../../test-workspace/tsc');
const shouldErrorDirs = [
'should-error',
'should-error-2',
'should-error-3',
];

function prettyPath(path: string, isRoot: boolean) {
const segments = path.split('/');
Expand All @@ -26,7 +31,7 @@ function collectTests(dir: string, depth = 2, isRoot: boolean = true): [filePath
for (const file of files) {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory() && file !== 'should-error') {
if (stat.isDirectory() && !shouldErrorDirs.includes(file)) {
const tsconfigPath = path.join(filePath, 'tsconfig.json');
if (fs.existsSync(tsconfigPath)) {
tests.push([
Expand Down Expand Up @@ -78,12 +83,14 @@ describe(`vue-tsc`, () => {
it(`vue-tsc no errors (${prettyPath(path, isRoot)})`, () => runVueTsc(path), 400_000);
}

it(`should throw an error when no vue-expect-error is used but the there is no error`, async () => {
try {
await runVueTsc(path.resolve(workspace, 'should-error'));
} catch (e) {
return;
}
throw new Error('Expected an error but got none');
});
for (const dir of shouldErrorDirs) {
it(`should throw an error when no vue-expect-error is used but the there is no error (${dir})`, async () => {
try {
await runVueTsc(path.resolve(workspace, dir));
} catch (e) {
return;
}
throw new Error('Expected an error but got none');
});
}
});
6 changes: 6 additions & 0 deletions test-workspace/tsc/should-error-2/main.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<!-- @vue-ignore -->
<template v-for="_a in 10" :foo="foo">
{{ foo }}
</template>
</template>
4 changes: 4 additions & 0 deletions test-workspace/tsc/should-error-2/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": [ "**/*" ]
}
6 changes: 6 additions & 0 deletions test-workspace/tsc/should-error-3/main.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<!-- @vue-error -->
<template v-if="true">
{{ foo }}
</template>
</template>
4 changes: 4 additions & 0 deletions test-workspace/tsc/should-error-3/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": [ "**/*" ]
}
3 changes: 3 additions & 0 deletions test-workspace/tsc/vue3/#4386/component.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script setup lang="ts">
defineProps<{ msg: string; closable: boolean; }>();
</script>
11 changes: 11 additions & 0 deletions test-workspace/tsc/vue3/#4386/main.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { ref } from 'vue';
import HelloWorld from './component.vue';
const items = ref([1, 2, 3, 4, 5]);
</script>

<template>
<HelloWorld msg="Vite + Vue" closable />
<HelloWorld v-for="id in items" :key="id" msg="`Vite - ${id}`" closable />
</template>

0 comments on commit 94c3386

Please sign in to comment.