typed-factorio
Version:
Featureful typescript definitions for the Factorio modding api.
218 lines (151 loc) • 9.16 kB
Markdown
# Typed Factorio
Complete and featureful typescript definitions for the Factorio modding lua API, for use with [TypescriptToLua](https://typescripttolua.github.io/).
This project aims to provide type definitions are as complete as possible.
The generator uses both the Factorio api docs JSON and manually defined additions.
## Installation
To use in your [TypescriptToLua](https://typescripttolua.github.io/) project:
1. Install this package: `npm install typed-factorio`
- Note: When types are updated for a new factorio version, you will need to update this package.
2. Add types for the [stages](https://lua-api.factorio.com/1.1.89/index.html) used to `tsconfig.json > compilerOptions > types`.
The available stages are `"typed-factorio/settings"`, `"typed-factorio/prototype"`, and `"typed-factorio/runtime"`.
Example:
```diff
// in tsconfig.json
{
"compilerOptions": {
+ "types": [ "typed-factorio/runtime" ]
}
}
```
The stages used will select the global variables defined.
You can include multiple stages, but there are some caveats. See [Using multiple stages in the same project](#using-multiple-stages-in-the-same-project) for more info.
## Usage notes
Here are some notes on using the types:
### Types for other stages
No matter which stage(s) are selected, _type_ definitions for all stages are available in the modules `"factorio:settings"`, `"factorio:prototype"`, and `"factorio:runtime"`:
```ts
import { BoolSettingDefinition } from "factorio:settings"
import { ItemPrototype } from "factorio:prototype"
import { LuaEntity } from "factorio:runtime"
```
You can also include just `"typed-factorio"` in your `types` field. This will include only global variables available to _all_ stages.
### `data.extend()` types
In the settings and prototype stages, the `data` global variable is available.
For [performance reasons](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-base-types-over-unions), `data.extend()` is by default loosely typed.
To get full type checking, you can use specific types in one of the following ways:
```ts
// Use `satisfies` to check types:
data.extend([
{
type: "ammo-category",
name: "foo",
} satisfies AmmoCategory,
{
type: "item",
name: "bar",
// ...other fields
} satisfies ItemPrototype,
])
// List types as a type argument to `extend`:
data.extend<AmmoCategory | ItemPrototype>([
{
type: "ammo-category",
name: "foo",
},
{
type: "item",
name: "bar",
// ...other fields
},
])
// Use types on separate variables:
const fooCategory: AmmoCategory = {
/* ... */
}
const barItem: ItemPrototype = {
/* ... */
}
data.extend([fooCategory, barItem])
```
### Factorio lualib modules
There are types for the following [Factorio lualib modules](https://github.com/wube/factorio-data/tree/master/core/lualib):
- `util`
- `mod-gui`
These can be imported as modules:
```ts
import * as util from "util"
const foo = util.copy(bar)
```
If you wish to see types for more lualib modules, feel free to open an issue or pull request.
### The `global` table
The `global` table (in the runtime stage) can have any shape, so it is not defined here. Instead, you can define it yourself:
- Add `declare const global: <Your type>` in a `.d.ts` file included in your project, to apply it project-wide.
- Add `declare const global: {...}` to each file where needed. This way, you can define only properties that each file specifically uses.
## Using multiple stages in the same project
Every Factorio loading stage declares different global variables.
To add types for multiple Factorio stages, you have a few options:
1. Add multiple stages to the "types" field, e.g. `"types": ["typed-factorio/prototype", "typed-factorio/runtime"]`. This will define global variables for _all_ stages selected. With this option, take care that you only use global variables available in the stages the code is run.
2. Add _only_ the runtime stage, then manually declare other global variables in files that use them. There are types in `"factorio:common"` to allow this:
```ts
// -- For the prototype stage --
import { PrototypeData, ActiveMods } from "factorio:common"
declare const data: PrototypeData
declare const mods: ActiveMods
// The `settings` global variable is already declared in the runtime stage.
// However, in the prototype stage _only_ `settings.startup` are available.
```
```ts
// -- For the settings stage --
import { SettingsData, ActiveMods } from "factorio:common"
declare const data: SettingsData
declare const mods: ActiveMods
```
3. Use a separate `tsconfig.json` for each stage. In each `tsconfig.json`, add only files in that stage to the `"include"` field, e.g. `include: ["src/control.ts"]` for the runtime stage. However, this means you need to run `tstl` separately for each stage, and files shared by multiple stages will be compiled multiple times.
## Type features
Here is some info on type features that you may find useful:
### `nil`
The `nil` type is equivalent to `undefined`.
A class attribute is marked as possibly nil if the _read_ type is possibly `nil`. For properties where `nil` is possible on _write_, but not _read_, you can use `undefined!` or `myNullableValue!`, e.g. `controlBehavior.parameters = undefined!`.
### Parameter Variants
Parameter tables with variants (having "additional attributes can be specified depending on type ...") are defined as a union of all variants. The type for a specific variant is prefixed with the variant name (e.g. `AmmoDamageTechnologyModifier`), or prefixed with "Other" for variants without extra properties (e.g. `OtherTechnologyModifier`).
### Events
Event IDs (`defines.events`) hold type info for their corresponding event type and filters, which is used by various methods in `script`.
You can pass an event data type parameter to `script.generate_event_name<T>()`, and it will return a `CustomEventId` that includes type info.
### Optional custominput name checking
You can optionally enable type-checking for custom input names (for `script.on_event` and `CustomInputPrototype`).
To do so, specify names by extending the CustomInputNames interface like so:
```ts
declare module "factorio:common" {
export interface CustomInputNames {
"my-custom-input": any
}
}
script.on_event("my-custom-input", () => {}) // type-checked
```
If not specified, `CustomInputName` defaults to just `string`.
### Array-like classes
Classes that have an index operator, a length operator, and have an array-like structure subclass from `(Readonly)Array`. These are `LuaInventory`, `LuaFluidBox`, `LuaTransportLine`.
This allows you to use these classes like arrays, e.g. having array methods and `.length` translating to the lua length operator. However, this means like typescript arrays, they are **0-indexed, not 1-indexed**.
### Read and write variants
For concepts that can take a table or array form, the main type (e.g. `MapPosition`) defines the table form, and an `Array` suffix (e.g. `MapPositionArray`) defines the array form.
Concepts in a "read" position are in table form, and concepts in a "write" position may be either in table or array form (e.g. `MapPosition | MapPositionArray`).
Concepts that include table-or-array concepts may have an additional `Write` variant (e.g. `ScriptArea`, `ScriptAreaWrite`).
### Classes with subclasses
Some classes have attributes that only work for particular subclasses. For these classes (e.g. `LuaEntity`) there are specific types that you can _optionally_ use:
- A "Base" type (e.g. `BaseEntity`) which contains only members usable by all subclasses
- Multiple subclass types, e.g. `CraftingMachineEntity`, which extends the base type with members specific to that subclass.
The original class name (e.g. `LuaEntity`) contains attributes for _all_ subclasses.
For stricter types, use the `Base` type generally, and the specific subclass type when needed.
You can also create your own type-narrowing functions:
```ts
function isCraftingMachineEntity(entity: BaseEntity): entity is CraftingMachineEntity {
return entity.type === "crafting-machine"
}
```
### LuaGuiElement
`LuaGuiElement` is broken up into a [discriminated union](https://basarat.gitbook.io/typescript/type-system/discriminated-unions), for each gui element type. Individual gui element types can be referred to by `<Type>GuiElement`, e.g. `ButtonGuiElement`.
Similarly, `GuiSpec` (the table passed to `LuaGuiElement.add`), is also a discriminated union. The type for a specific GuiSpec is `<Type>GuiSpec`, e.g. `ListBoxGuiSpec`. `LuaGuiElement.add` will return the appropriate gui element type corresponding to the GuiSpec type passed in.
This is done both to provide more accurate types, and for possible integration with [JSX](https://typescripttolua.github.io/docs/jsx/).
## Support
If you find this project useful, consider tipping me on Kofi!
<a href='https://ko-fi.com/Z8Z1VI6P8' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi2.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>