Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0d9745e
wip
markwpearce Dec 5, 2025
4849aad
Merge branch 'v1' into intersection_type
markwpearce Dec 5, 2025
a742506
Merge branch 'v1' into intersection_type
markwpearce Dec 15, 2025
8fde59b
Added IntersectionType tests
markwpearce Dec 15, 2025
48ac3f0
Added support for grouped type expressions
markwpearce Dec 15, 2025
a602261
Merge branch 'v1' into intersection_type
markwpearce Dec 15, 2025
723b945
Adding tests for member types of intersection types in scope contexts
markwpearce Dec 17, 2025
e202859
Adds code to handle order of operations when building types
markwpearce Jan 1, 2026
02ba2bb
Adds code to handle order of operations when building types
markwpearce Jan 1, 2026
29022b7
removed log statments, started adding validation tests
markwpearce Jan 2, 2026
6a73946
remove .only
markwpearce Jan 2, 2026
e49662b
Better handling of errors - missing right paren on function declratio…
markwpearce Jan 5, 2026
8f72d55
fix lint error
markwpearce Jan 5, 2026
e7a8074
Adds more validation and compatibility tests for intersections:
markwpearce Jan 6, 2026
9305d48
Adds new reference type for intersections with types with default dyn…
markwpearce Jan 9, 2026
a7c0f7b
Added comment
markwpearce Jan 9, 2026
b288f8a
Added docs
markwpearce Jan 12, 2026
7836adf
Added completion tests
markwpearce Jan 13, 2026
bbd1bec
Renamed 'ComplexType' -> 'CompoundType'
markwpearce Jan 13, 2026
492e7ad
More tests for Intersections with order of operations and callfuncs
markwpearce Jan 13, 2026
5e648aa
Merge branch 'v1' into intersection_type
markwpearce Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions docs/intersection-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Intersection Types

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).

## Syntax

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:

```BrighterScript
interface HasId
id as string
end interface

interface HasUrl
url as string
end interface

function getUrlWithQueryId(value as HasId and HasUrl) as string
return value.url + "?id=" + value.id
end function
```

Any number of inner types, including classes or interfaces, could be part of an intersection:

```BrighterScript
interface HasId
id as string
end interface

interface HasUrl
url as string
end interface

interface HasSize
width as integer
height as integer
end interface


function getUrlWithQuerySize(response as HasId and HasUrl and HasSize) as string
return value.url + "?id=" + value.id + "&w=" + value.width.toStr().trim() + "&h=" + value.height.toStr().trim()
end function
```

## Members and Validation

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.

```BrighterScript
sub testIntersection(value as {id as string} and {id as integer})
' This is an error - "value.id" is of type "string AND integer"
printInteger(value.id)
end sub

sub printInteger(x as integer)
print x
end sub
```

## Transpilation

Since Brightscript does not have intersection types natively, intersection types will be transpiled as `dynamic`.

```BrighterScript

interface HasRadius
radius as float
end interface

interface Point
x as float
y as float
end interface

function getCircleDetails(circle as HasRadius and Point) as string
return "Circle: radius=" + circle.radius.toStr() + ", center=" + circle.x.toStr() + "," + circle.y.toStr()
end function
```

transpiles to

```BrightScript
function getCircleDetails(circle as dynamic) as string
return "Circle: radius=" + circle.radius.ToStr() + ", center=" + circle.x.toStr() + "," + circle.y.toStr()
end function
```
60 changes: 52 additions & 8 deletions docs/readme.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# BrighterScript

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.

See the following pages for more information:

## [Annotations](annotations.md)

```brighterscript
'mostly useful for plugins that change code based on annotations
@logOnException()
Expand All @@ -13,12 +15,14 @@ end
```

## [Callfunc Operator](callfunc-operator.md)

```brighterscript
'instead of `node.callfunc("someMethod", 1, 2, 3)`, you can do this:
[email protected](1, 2, 3)
```

## [Classes](classes.md)

```brighterscript
class Movie
public title as string
Expand All @@ -33,6 +37,7 @@ end class
```

## [Constants](constants.md)

```brighterscript
const API_URL = "https://api.acme.com/v1/"
sub main()
Expand All @@ -41,6 +46,7 @@ end sub
```

## [Enums](enums.md)

```brighterscript
enum RemoteButton
up = "up"
Expand All @@ -51,6 +57,7 @@ end enum
```

## [Exceptions](exceptions.md)

```brighterscript
try
somethingDangerous()
Expand All @@ -59,7 +66,40 @@ catch 'look, no exception variable!
end try
```

## [Imports](imports.md)

```brighterscript
import "pkg:/source/util.bs"
sub main()
print util_toUpper("hello world")
end sub
```

## [Interfaces](interfaces.md)

```brighterscript
interface IMyComponent
top as roSGNodeMyComponent

isSelected as boolean
selectedIndex as integer

data as {id as string, isEpisode as boolean}
end interface
```

## [Intersection Types](intersection-types.md)

```brighterscript
type MyClassAA = MyClass and roAssociativeArray

sub addData(klass as MyClass and roAssociativeArray, data as roAssociativeArray)
return klass.append(data)
end sub
```

## [Namespaces](namespaces.md)

```brighterscript
namespace util
function toUpper(value as string)
Expand All @@ -72,28 +112,24 @@ sub main()
end sub
```

## [Imports](imports.md)
```brighterscript
import "pkg:/source/util.bs"
sub main()
print util_toUpper("hello world")
end sub
```

## [Null-coalescing operator](null-coalescing-operator.md)

```brighterscript
userSettings = getSettingsFromRegistry() ?? {}
```

## [Plugins](plugins.md)

Plugins can be used to manipulate code at any point during the program lifecycle.

## [Regular Expression Literals](regex-literals.md)

```brighterscript
print /hello world/ig
```

## [Source Literals](source-literals.md)

```brighterscript
print SOURCE_FILE_PATH
print SOURCE_LINE_NUM
Expand All @@ -103,7 +139,9 @@ print SOURCE_LOCATION
print PKG_PATH
print PKG_LOCATION
```

## [Template Strings (Template Literals)](template-strings.md)

```brighterscript
name = `John Smith`

Expand All @@ -114,16 +152,19 @@ second line text`
```

## [Ternary (Conditional) Operator](ternary-operator.md)

```brighterscript
authStatus = user <> invalid ? "logged in" : "not logged in"
```

## [Typecasts](typecasts.md)

```BrighterScript
nodeId = (node as roSgNode).id
```

## [Typed Arrays](typed-arrays.md)

```brighterscript
function getY(translation as float[]) as float
yValue = -1
Expand All @@ -135,6 +176,7 @@ end function
```

## [Type Statements](type-statements.md)

```brighterscript
type number = integer or float or double

Expand All @@ -144,11 +186,13 @@ end function
```

## [Union Types](union-types.md)

```brighterscript
sub logData(data as string or number)
print data.toStr()
end sub
```

## [Variable Shadowing](variable-shadowing.md)

Name resolution rules for various types of shadowing.
4 changes: 2 additions & 2 deletions src/CrossScopeValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { ReferenceType } from './types/ReferenceType';
import { getAllRequiredSymbolNames } from './types/ReferenceType';
import type { TypeChainEntry, TypeChainProcessResult } from './interfaces';
import { BscTypeKind } from './types/BscTypeKind';
import { getAllTypesFromUnionType } from './types/helpers';
import { getAllTypesFromCompoundType } from './types/helpers';
import type { BscType } from './types/BscType';
import type { BscFile } from './files/BscFile';
import type { ClassStatement, ConstStatement, EnumMemberStatement, EnumStatement, InterfaceStatement, NamespaceStatement } from './parser/Statement';
Expand Down Expand Up @@ -222,7 +222,7 @@ export class CrossScopeValidator {
}

if (isUnionType(symbol.typeChain[0].type) && symbol.typeChain[0].data.isInstance) {
const allUnifiedTypes = getAllTypesFromUnionType(symbol.typeChain[0].type);
const allUnifiedTypes = getAllTypesFromCompoundType(symbol.typeChain[0].type);
for (const unifiedType of allUnifiedTypes) {
unnamespacedNameLowers.push(joinTypeChainForKey(symbol.typeChain, unifiedType));
}
Expand Down
Loading