Skip to content

Commit 610607e

Browse files
authored
Intersection Type (#1603)
1 parent 9ad45b3 commit 610607e

26 files changed

+2103
-93
lines changed

docs/intersection-types.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Intersection Types
2+
3+
BrighterScript Intersection Types are a way to define a type that combines the members of multiple types. They are similar to Intersection Types found in other languages, such as [TypeScript](https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types).
4+
5+
## Syntax
6+
7+
Intersection types can be declared with the following syntax: `<type> and <type>`. For example, the parameter to the function below could be meets both the interfaces `HasId` and `HasUrl`:
8+
9+
```BrighterScript
10+
interface HasId
11+
id as string
12+
end interface
13+
14+
interface HasUrl
15+
url as string
16+
end interface
17+
18+
function getUrlWithQueryId(value as HasId and HasUrl) as string
19+
return value.url + "?id=" + value.id
20+
end function
21+
```
22+
23+
Any number of inner types, including classes or interfaces, could be part of an intersection:
24+
25+
```BrighterScript
26+
interface HasId
27+
id as string
28+
end interface
29+
30+
interface HasUrl
31+
url as string
32+
end interface
33+
34+
interface HasSize
35+
width as integer
36+
height as integer
37+
end interface
38+
39+
40+
function getUrlWithQuerySize(response as HasId and HasUrl and HasSize) as string
41+
return value.url + "?id=" + value.id + "&w=" + value.width.toStr().trim() + "&h=" + value.height.toStr().trim()
42+
end function
43+
```
44+
45+
## Members and Validation
46+
47+
A diagnostic error will be raised when a member is accessed that is not a member of any of the types of a union. Note also that if a member is not the same type in each of the types in the union, it will itself be considered an intersection.
48+
49+
```BrighterScript
50+
sub testIntersection(value as {id as string} and {id as integer})
51+
' This is an error - "value.id" is of type "string AND integer"
52+
printInteger(value.id)
53+
end sub
54+
55+
sub printInteger(x as integer)
56+
print x
57+
end sub
58+
```
59+
60+
## Transpilation
61+
62+
Since Brightscript does not have intersection types natively, intersection types will be transpiled as `dynamic`.
63+
64+
```BrighterScript
65+
66+
interface HasRadius
67+
radius as float
68+
end interface
69+
70+
interface Point
71+
x as float
72+
y as float
73+
end interface
74+
75+
function getCircleDetails(circle as HasRadius and Point) as string
76+
return "Circle: radius=" + circle.radius.toStr() + ", center=" + circle.x.toStr() + "," + circle.y.toStr()
77+
end function
78+
```
79+
80+
transpiles to
81+
82+
```BrightScript
83+
function getCircleDetails(circle as dynamic) as string
84+
return "Circle: radius=" + circle.radius.ToStr() + ", center=" + circle.x.toStr() + "," + circle.y.toStr()
85+
end function
86+
```

docs/readme.md

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# BrighterScript
2+
23
BrighterScript is a superset of Roku's BrightScript language. Its goal is to provide new functionality and enhanced syntax support to enhance the Roku channel developer experience.
34

45
See the following pages for more information:
56

67
## [Annotations](annotations.md)
8+
79
```brighterscript
810
'mostly useful for plugins that change code based on annotations
911
@logOnException()
@@ -13,12 +15,14 @@ end
1315
```
1416

1517
## [Callfunc Operator](callfunc-operator.md)
18+
1619
```brighterscript
1720
'instead of `node.callfunc("someMethod", 1, 2, 3)`, you can do this:
1821
node@.someMethod(1, 2, 3)
1922
```
2023

2124
## [Classes](classes.md)
25+
2226
```brighterscript
2327
class Movie
2428
public title as string
@@ -33,6 +37,7 @@ end class
3337
```
3438

3539
## [Constants](constants.md)
40+
3641
```brighterscript
3742
const API_URL = "https://api.acme.com/v1/"
3843
sub main()
@@ -41,6 +46,7 @@ end sub
4146
```
4247

4348
## [Enums](enums.md)
49+
4450
```brighterscript
4551
enum RemoteButton
4652
up = "up"
@@ -51,6 +57,7 @@ end enum
5157
```
5258

5359
## [Exceptions](exceptions.md)
60+
5461
```brighterscript
5562
try
5663
somethingDangerous()
@@ -59,7 +66,40 @@ catch 'look, no exception variable!
5966
end try
6067
```
6168

69+
## [Imports](imports.md)
70+
71+
```brighterscript
72+
import "pkg:/source/util.bs"
73+
sub main()
74+
print util_toUpper("hello world")
75+
end sub
76+
```
77+
78+
## [Interfaces](interfaces.md)
79+
80+
```brighterscript
81+
interface IMyComponent
82+
top as roSGNodeMyComponent
83+
84+
isSelected as boolean
85+
selectedIndex as integer
86+
87+
data as {id as string, isEpisode as boolean}
88+
end interface
89+
```
90+
91+
## [Intersection Types](intersection-types.md)
92+
93+
```brighterscript
94+
type MyClassAA = MyClass and roAssociativeArray
95+
96+
sub addData(klass as MyClass and roAssociativeArray, data as roAssociativeArray)
97+
return klass.append(data)
98+
end sub
99+
```
100+
62101
## [Namespaces](namespaces.md)
102+
63103
```brighterscript
64104
namespace util
65105
function toUpper(value as string)
@@ -72,28 +112,24 @@ sub main()
72112
end sub
73113
```
74114

75-
## [Imports](imports.md)
76-
```brighterscript
77-
import "pkg:/source/util.bs"
78-
sub main()
79-
print util_toUpper("hello world")
80-
end sub
81-
```
82-
83115
## [Null-coalescing operator](null-coalescing-operator.md)
116+
84117
```brighterscript
85118
userSettings = getSettingsFromRegistry() ?? {}
86119
```
87120

88121
## [Plugins](plugins.md)
122+
89123
Plugins can be used to manipulate code at any point during the program lifecycle.
90124

91125
## [Regular Expression Literals](regex-literals.md)
126+
92127
```brighterscript
93128
print /hello world/ig
94129
```
95130

96131
## [Source Literals](source-literals.md)
132+
97133
```brighterscript
98134
print SOURCE_FILE_PATH
99135
print SOURCE_LINE_NUM
@@ -103,7 +139,9 @@ print SOURCE_LOCATION
103139
print PKG_PATH
104140
print PKG_LOCATION
105141
```
142+
106143
## [Template Strings (Template Literals)](template-strings.md)
144+
107145
```brighterscript
108146
name = `John Smith`
109147
@@ -114,16 +152,19 @@ second line text`
114152
```
115153

116154
## [Ternary (Conditional) Operator](ternary-operator.md)
155+
117156
```brighterscript
118157
authStatus = user <> invalid ? "logged in" : "not logged in"
119158
```
120159

121160
## [Typecasts](typecasts.md)
161+
122162
```BrighterScript
123163
nodeId = (node as roSgNode).id
124164
```
125165

126166
## [Typed Arrays](typed-arrays.md)
167+
127168
```brighterscript
128169
function getY(translation as float[]) as float
129170
yValue = -1
@@ -135,6 +176,7 @@ end function
135176
```
136177

137178
## [Type Statements](type-statements.md)
179+
138180
```brighterscript
139181
type number = integer or float or double
140182
@@ -144,11 +186,13 @@ end function
144186
```
145187

146188
## [Union Types](union-types.md)
189+
147190
```brighterscript
148191
sub logData(data as string or number)
149192
print data.toStr()
150193
end sub
151194
```
152195

153196
## [Variable Shadowing](variable-shadowing.md)
197+
154198
Name resolution rules for various types of shadowing.

src/CrossScopeValidator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { ReferenceType } from './types/ReferenceType';
1111
import { getAllRequiredSymbolNames } from './types/ReferenceType';
1212
import type { TypeChainEntry, TypeChainProcessResult } from './interfaces';
1313
import { BscTypeKind } from './types/BscTypeKind';
14-
import { getAllTypesFromUnionType } from './types/helpers';
14+
import { getAllTypesFromCompoundType } from './types/helpers';
1515
import type { BscType } from './types/BscType';
1616
import type { BscFile } from './files/BscFile';
1717
import type { ClassStatement, ConstStatement, EnumMemberStatement, EnumStatement, InterfaceStatement, NamespaceStatement } from './parser/Statement';
@@ -222,7 +222,7 @@ export class CrossScopeValidator {
222222
}
223223

224224
if (isUnionType(symbol.typeChain[0].type) && symbol.typeChain[0].data.isInstance) {
225-
const allUnifiedTypes = getAllTypesFromUnionType(symbol.typeChain[0].type);
225+
const allUnifiedTypes = getAllTypesFromCompoundType(symbol.typeChain[0].type);
226226
for (const unifiedType of allUnifiedTypes) {
227227
unnamespacedNameLowers.push(joinTypeChainForKey(symbol.typeChain, unifiedType));
228228
}

0 commit comments

Comments
 (0)