Skip to content

Commit bc73bae

Browse files
committed
Check unassignable types and properties
1 parent a3cbd8d commit bc73bae

File tree

4 files changed

+126
-8
lines changed

4 files changed

+126
-8
lines changed

draft/object-types.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ Check phases
577577
Type {X} is not assignable to {Y}
578578
```
579579

580-
2. Go through the list of properties. For each one, check if it's defined in the type definition. If it's not, it should generate the type error
580+
2. Go through the list of properties. For each one, check if it's defined in the type definition. If it's not, it should generate the type error. This error only appears if rule number one passes the checker.
581581

582582
```
583583
Object literal may only specify known properties, and 'g' does not exist in type 'D'.

src/check.ts

+71-1
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,48 @@ export function check(module: Module) {
138138

139139
const type = checkType(declaration.typename);
140140

141-
if (type !== initType && type !== errorType)
141+
handleUnassignableTypes(declaration, initType, type);
142+
143+
if (initType.id === 'object') {
144+
// Handle property type mismatch error
145+
handlePropertyTypeMismatch(declaration, initType, type);
146+
// Handle only known property error
147+
// Handle missing properties error
148+
149+
return type;
150+
}
151+
152+
if (type !== initType && type !== errorType) {
142153
error(
143154
declaration.init.pos,
144155
`Cannot assign initialiser of type '${typeToString(
145156
initType,
146157
)}' to variable with declared type '${typeToString(type)}'.`,
147158
);
159+
}
148160

149161
return type;
150162
}
151163

164+
function handlePropertyTypeMismatch(
165+
declaration: VariableDeclaration,
166+
initType: Type,
167+
type: Type,
168+
) {
169+
for (const [propertyName, propertyType] of initType.members as TypeTable) {
170+
const typePropertyType = type.members?.get(propertyName);
171+
172+
if (typePropertyType) {
173+
handleUnassignablePropertyTypes(
174+
declaration,
175+
propertyType,
176+
typePropertyType,
177+
propertyName,
178+
);
179+
}
180+
}
181+
}
182+
152183
function checkVariableDeclarationType(declaration: VariableDeclaration) {
153184
return declaration.typename
154185
? checkType(declaration.typename)
@@ -220,6 +251,45 @@ export function check(module: Module) {
220251
: checkExpression(statement.init);
221252
}
222253

254+
function handleUnassignableTypes(
255+
declaration: VariableDeclaration,
256+
initType: Type,
257+
type: Type,
258+
) {
259+
if (initType.id !== type.id) {
260+
error(
261+
declaration.init.pos,
262+
`Type '${typeToString(
263+
initType,
264+
)}' is not assignable to type '${typeToString(type)}'.`,
265+
);
266+
}
267+
}
268+
269+
function handleUnassignablePropertyTypes(
270+
declaration: VariableDeclaration,
271+
initType: Type,
272+
type: Type,
273+
propertyName: string,
274+
) {
275+
if (initType.id !== type.id) {
276+
error(
277+
declaration.init.pos,
278+
`Type '${typeToString(
279+
initType,
280+
)}' is not assignable to type '${typeToString(
281+
type,
282+
)}'. The expected type comes from property '${propertyName}' which is declared here on type '${
283+
declaration.typename?.text
284+
}'`,
285+
);
286+
}
287+
288+
if (initType.id === type.id && initType.id === 'object') {
289+
handlePropertyTypeMismatch(declaration, initType, type);
290+
}
291+
}
292+
223293
function handleSubsequentVariableDeclarationsTypes(
224294
declaration: VariableDeclaration,
225295
valueDeclarationType: Type,

tests/objectLiteral.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@ type D = {
1717
};
1818
};
1919

20+
var a: A = 'string';
21+
var b: B = 10;
2022
var c: C = {
21-
a: 'a',
22-
b: 1
23+
a: a,
24+
b: b
2325
};
2426

2527
var d: D = {
26-
a: 'a',
27-
b: 1,
28-
c: 'a',
29-
d: 1,
28+
a: a,
29+
b: b,
30+
c: a,
31+
d: b,
3032
e: c,
3133
f: {
3234
foo: 'string',

tests/unassignableObjectLiteral.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
type A = string;
2+
type B = number;
3+
type C = {
4+
a: string;
5+
b: number;
6+
};
7+
8+
type D = {
9+
a: string;
10+
b: number;
11+
c: A;
12+
d: B;
13+
e: C;
14+
f: {
15+
foo: string;
16+
bar: number;
17+
};
18+
};
19+
20+
var a: D = 'string';
21+
var b: D = 1;
22+
var c: D = {
23+
a: 1
24+
}
25+
26+
var d: D = {
27+
f: {
28+
foo: 123
29+
}
30+
}
31+
32+
type E = {
33+
a: {
34+
foo: {
35+
bar: number
36+
}
37+
}
38+
}
39+
40+
var e: E = {
41+
a: {
42+
foo: {
43+
bar: '123'
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)