• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / brighterscript / #15069

09 Jan 2026 02:58PM UTC coverage: 86.959% (-0.01%) from 86.973%
#15069

push

web-flow
Merge a7c0f7b34 into 2ea4d2108

14543 of 17676 branches covered (82.28%)

Branch coverage included in aggregate %.

240 of 319 new or added lines in 14 files covered. (75.24%)

208 existing lines in 11 files now uncovered.

15289 of 16630 relevant lines covered (91.94%)

24236.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.02
/src/types/IntersectionType.ts
1
import type { GetTypeOptions, TypeCompatibilityData } from '../interfaces';
2
import { isDynamicType, isIntersectionType, isObjectType, isTypedFunctionType } from '../astUtils/reflection';
1✔
3
import { BscType } from './BscType';
1✔
4
import { IntersectionWithDefaultDynamicReferenceType, ReferenceType } from './ReferenceType';
1✔
5
import { addAssociatedTypesTableAsSiblingToMemberTable, getAllTypesFromComplexType, isEnumTypeCompatible, isTypeWithPotentialDefaultDynamicMember, joinTypesString, reduceTypesForIntersectionType } from './helpers';
1✔
6
import { BscTypeKind } from './BscTypeKind';
1✔
7
import type { TypeCacheEntry } from '../SymbolTable';
8
import { SymbolTable } from '../SymbolTable';
1✔
9
import { SymbolTypeFlag } from '../SymbolTypeFlag';
1✔
10
import { BuiltInInterfaceAdder } from './BuiltInInterfaceAdder';
1✔
11
import { util } from '../util';
1✔
12
import { DynamicType } from './DynamicType';
1✔
13

14
export function intersectionTypeFactory(types: BscType[]) {
1✔
NEW
15
    return new IntersectionType(types);
×
16
}
17

18
export class IntersectionType extends BscType {
1✔
19
    constructor(
20
        public types: BscType[]
103✔
21
    ) {
22
        super(joinTypesString(types, 'and', BscTypeKind.IntersectionType));
103✔
23
        this.callFuncAssociatedTypesTable = new SymbolTable(`Intersection: CallFuncAssociatedTypes`);
103✔
24
    }
25

26
    public readonly kind = BscTypeKind.IntersectionType;
103✔
27

28
    public readonly callFuncAssociatedTypesTable: SymbolTable;
29

30
    public addType(type: BscType) {
NEW
31
        this.types.push(type);
×
32
    }
33

34
    isResolvable(): boolean {
35
        for (const type of this.types) {
69✔
36
            // resolvable if any inner type is resolvable
37
            if (type.isResolvable()) {
72✔
38
                return true;
66✔
39
            }
40
        }
41
        return false;
3✔
42
    }
43

44
    private getMemberTypeFromInnerTypes(name: string, options: GetTypeOptions): BscType {
45
        const typeFromMembers = this.types.map((innerType) => {
294✔
46
            return innerType?.getMemberType(name, { ...options, ignoreDefaultDynamicMembers: true });
588!
47
        });
48
        let filteredTypes = reduceTypesForIntersectionType(typeFromMembers.map(t => t).filter(t => t !== undefined));
588✔
49

50
        if (filteredTypes.length === 0 && this.types.some(isTypeWithPotentialDefaultDynamicMember)) {
294!
NEW
51
            const typesFromMembersWithDynamicAA = this.types.map((innerType) => {
×
NEW
52
                return innerType?.getMemberType(name, options);
×
53
            });
NEW
54
            filteredTypes = reduceTypesForIntersectionType(typesFromMembersWithDynamicAA.map(t => t).filter(t => t !== undefined));
×
55
        }
56
        if (filteredTypes.length === 0) {
294✔
57
            return undefined;
68✔
58
        } else if (filteredTypes.length === 1) {
226✔
59
            return filteredTypes[0];
223✔
60
        }
61
        return new IntersectionType(filteredTypes);
3✔
62
    }
63

64
    private getCallFuncFromInnerTypes(name: string, options: GetTypeOptions): BscType {
NEW
65
        const typeFromMembers = reduceTypesForIntersectionType(this.types.map((innerType) => innerType?.getCallFuncType(name, options)).filter(t => t !== undefined));
×
66

NEW
67
        if (typeFromMembers.length === 0) {
×
NEW
68
            return undefined;
×
NEW
69
        } else if (typeFromMembers.length === 1) {
×
NEW
70
            return typeFromMembers[0];
×
71
        }
NEW
72
        return new IntersectionType(typeFromMembers);
×
73
    }
74

75
    getMemberType(name: string, options: GetTypeOptions) {
76
        const innerTypesMemberType = this.getMemberTypeFromInnerTypes(name, options);
138✔
77
        if (!innerTypesMemberType) {
138✔
78
            // We don't have any members of any inner types that match
79
            // so instead, create reference type that will
80
            return new ReferenceType(name, name, options.flags, () => {
11✔
81
                return {
106✔
82
                    name: `IntersectionType MemberTable: '${this.__identifier}'`,
83
                    getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
84
                        const referenceTypeInnerMemberTypes = this.getMemberTypeFromInnerTypes(name, options);
106✔
85
                        if (!referenceTypeInnerMemberTypes) {
106✔
86
                            if (this.hasMemberTypeWithDefaultDynamicMember && !innerOptions.ignoreDefaultDynamicMembers) {
57!
NEW
87
                                return DynamicType.instance;
×
88
                            }
89
                            return undefined;
57✔
90
                        }
91
                        return referenceTypeInnerMemberTypes;
49✔
92
                    },
93
                    setCachedType: (innerName: string, innerCacheEntry: TypeCacheEntry, innerOptions: GetTypeOptions) => {
94
                        // TODO: is this even cachable? This is a NO-OP for now, and it shouldn't hurt anything
95
                    },
96
                    addSibling: (symbolTable: SymbolTable) => {
97
                        // TODO: I don't know what this means in this context?
98
                    }
99
                };
100
            });
101
        }
102
        if (!innerTypesMemberType?.isResolvable()) {
127!
103
            const shouldCreateDynamicAAMember = this.hasMemberTypeWithDefaultDynamicMember && !options.ignoreDefaultDynamicMembers;
26✔
104
            if (shouldCreateDynamicAAMember) {
26!
105
                return new IntersectionWithDefaultDynamicReferenceType(innerTypesMemberType);
26✔
106
            }
107
        }
108
        return innerTypesMemberType;
101✔
109
    }
110

111
    getCallFuncType(name: string, options: GetTypeOptions) {
NEW
112
        const resultCallFuncType = this.getCallFuncFromInnerTypes(name, options);
×
NEW
113
        if (!resultCallFuncType) {
×
114
            // We don't have any members of any inner types that match
115
            // so instead, create reference type that will
NEW
116
            return new ReferenceType(name, name, options.flags, () => {
×
NEW
117
                return {
×
118
                    name: `IntersectionType CallFunc MemberTable: '${this.__identifier}'`,
119
                    getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
NEW
120
                        const referenceTypeInnerMemberType = this.getCallFuncFromInnerTypes(name, options);
×
NEW
121
                        if (!referenceTypeInnerMemberType) {
×
NEW
122
                            if (this.hasMemberTypeWithDefaultDynamicMember && !innerOptions.ignoreDefaultDynamicMembers) {
×
NEW
123
                                return DynamicType.instance;
×
124
                            }
125
                        }
NEW
126
                        return referenceTypeInnerMemberType;
×
127
                    },
128
                    setCachedType: (innerName: string, innerCacheEntry: TypeCacheEntry, innerOptions: GetTypeOptions) => {
129
                        // TODO: is this even cachable? This is a NO-OP for now, and it shouldn't hurt anything
130
                    },
131
                    addSibling: (symbolTable: SymbolTable) => {
132
                        // TODO: I don't know what this means in this context?
133
                    }
134
                };
135
            });
136
        }
137

NEW
138
        if (!resultCallFuncType?.isResolvable()) {
×
NEW
139
            const shouldCreateDynamicAAMember = this.hasMemberTypeWithDefaultDynamicMember && !options.ignoreDefaultDynamicMembers;
×
NEW
140
            if (shouldCreateDynamicAAMember) {
×
NEW
141
                return new IntersectionWithDefaultDynamicReferenceType(resultCallFuncType);
×
142
            }
143
        }
144

NEW
145
        if (isTypedFunctionType(resultCallFuncType)) {
×
NEW
146
            const typesToCheck = [...resultCallFuncType.params.map(p => p.type), resultCallFuncType.returnType];
×
147

NEW
148
            for (const type of typesToCheck) {
×
NEW
149
                addAssociatedTypesTableAsSiblingToMemberTable(type, this.callFuncAssociatedTypesTable, SymbolTypeFlag.runtime);
×
150
            }
151
        }
NEW
152
        return resultCallFuncType;
×
153
    }
154

155
    get returnType() {
NEW
156
        return util.getReturnTypeOfIntersectionOfFunctions(this);
×
157
    }
158

159

160
    isTypeCompatible(targetType: BscType, data?: TypeCompatibilityData): boolean {
161
        if (isDynamicType(targetType) || isObjectType(targetType) || this === targetType) {
13!
NEW
162
            return true;
×
163
        }
164
        if (isEnumTypeCompatible(this, targetType, data)) {
13!
NEW
165
            return true;
×
166
        }
167
        if (isIntersectionType(targetType)) {
13✔
168
            // check if this all the types of this type are in the target (eg, target is a super set of this types)
169
            let allMembersSatisfied = true;
6✔
170
            for (const memberType of this.types) {
6✔
171
                let foundCompatibleInnerType = false;
14✔
172
                for (const targetInnerType of targetType.types) {
14✔
173
                    if (memberType.isTypeCompatible(targetInnerType, data)) {
25✔
174
                        foundCompatibleInnerType = true;
11✔
175
                        break;
11✔
176
                    }
177
                }
178
                if (!foundCompatibleInnerType) {
14✔
179
                    allMembersSatisfied = false;
3✔
180
                }
181
            }
182
            return allMembersSatisfied;
6✔
183
        }
184
        let foundCompatibleInnerType = true;
7✔
185
        for (const innerType of this.types) {
7✔
186
            if (!innerType.isTypeCompatible(targetType, data)) {
14✔
187
                foundCompatibleInnerType = false;
6✔
188
            }
189
        }
190

191
        return foundCompatibleInnerType;
7✔
192
    }
193
    toString(): string {
194
        return joinTypesString(this.types, 'and', BscTypeKind.IntersectionType);
6✔
195
    }
196

197
    /**
198
     * Used for transpilation
199
     */
200
    toTypeString(): string {
NEW
201
        const uniqueTypeStrings = new Set<string>(getAllTypesFromComplexType(this).map(t => t.toTypeString()));
×
202

NEW
203
        if (uniqueTypeStrings.size === 1) {
×
NEW
204
            return uniqueTypeStrings.values().next().value;
×
205
        }
NEW
206
        return 'dynamic';
×
207
    }
208

209
    checkAllMemberTypes(predicate: (BscType) => boolean) {
NEW
210
        return this.types.reduce((acc, type) => {
×
NEW
211
            return acc && predicate(type);
×
212
        }, true);
213
    }
214

215
    isEqual(targetType: BscType): boolean {
216
        if (!isIntersectionType(targetType)) {
1!
NEW
217
            return false;
×
218
        }
219
        if (this === targetType) {
1!
NEW
220
            return true;
×
221
        }
222
        for (const type of this.types) {
1✔
223
            let foundMatch = false;
3✔
224
            for (const targetTypeInner of targetType.types) {
3✔
225
                if (type.isEqual(targetTypeInner)) {
6✔
226
                    foundMatch = true;
3✔
227
                    break;
3✔
228
                }
229
            }
230
            if (!foundMatch) {
3!
NEW
231
                return false;
×
232
            }
233
        }
234
        return true;
1✔
235
    }
236

237
    getMemberTable(): SymbolTable {
238
        const intersectionTable = new SymbolTable(this.__identifier + ' IntersectionTable');
6✔
239

240
        for (const type of this.types) {
6✔
241
            type.addBuiltInInterfaces();
12✔
242
            for (const symbol of type.getMemberTable().getAllSymbols(SymbolTypeFlag.runtime)) {
12✔
243
                const foundType = this.getMemberTypeFromInnerTypes(symbol.name, { flags: SymbolTypeFlag.runtime });
44✔
244
                intersectionTable.addSymbol(symbol.name, {}, foundType, SymbolTypeFlag.runtime);
44✔
245
            }
246
        }
247
        const firstType = this.types[0];
6✔
248
        if (!firstType) {
6!
NEW
249
            return intersectionTable;
×
250
        }
251
        firstType.addBuiltInInterfaces();
6✔
252
        for (const symbol of firstType.getMemberTable().getAllSymbols(SymbolTypeFlag.runtime)) {
6✔
253
            const foundType = this.getMemberTypeFromInnerTypes(symbol.name, { flags: SymbolTypeFlag.runtime });
6✔
254
            intersectionTable.addSymbol(symbol.name, {}, foundType, SymbolTypeFlag.runtime);
6✔
255
        }
256
        return intersectionTable;
6✔
257
    }
258

259

260
    private _hasMemberTypeWithDefaultDynamicMember: boolean = undefined;
103✔
261
    get hasMemberTypeWithDefaultDynamicMember(): boolean {
262
        if (this._hasMemberTypeWithDefaultDynamicMember !== undefined) {
83✔
263
            return this._hasMemberTypeWithDefaultDynamicMember;
73✔
264
        }
265
        this._hasMemberTypeWithDefaultDynamicMember = false;
10✔
266

267
        for (const type of this.types) {
10✔
268
            if (isTypeWithPotentialDefaultDynamicMember(type)) {
19✔
269
                this._hasMemberTypeWithDefaultDynamicMember = true;
6✔
270
                break;
6✔
271
            }
272
        }
273
        return this._hasMemberTypeWithDefaultDynamicMember;
10✔
274
    }
275
}
276

277
BuiltInInterfaceAdder.intersectionTypeFactory = (types: BscType[]) => {
1✔
NEW
278
    return new IntersectionType(types);
×
279
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc