typed-factorio
Version:
Featureful typescript definitions for the Factorio modding api.
224 lines (156 loc) • 9.87 kB
Markdown
[](https://www.npmjs.com/package/typed-factorio) 
# 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 that are as complete as possible.
## Installation
To use in your [TypescriptToLua](https://typescripttolua.github.io/) project:
1. Install this package: `npm install --save-dev typed-factorio`
> Note: When types are updated for a new factorio version, you will need to update this package.
2. Add types for the [Factorio stages](https://lua-api.factorio.com/latest) used to your `tsconfig.json` under `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" ]
}
}
```
Each stage will define the global variables used in that stage.
You can include multiple stages, but there are some caveats. For more info, see [Using multiple stages in the same project](#using-multiple-loading-stages-in-the-same-project).
## Usage notes
Some more info on using 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 tsconfig's `types`. This will define only global variables that are available to _all_ stages.
### The `storage` table
The `storage` table (in the runtime stage) can have any shape, so it is not defined here. Instead, you can define it yourself:
- Add `declare const storage: <Your type>` in a `.d.ts` file. Make sure this file is included by your tsconfig!
- Add `declare global { const storage: <Your type> }` in a `.ts` file included in your project.
- Add `declare const storage: {...}` to each file where needed. This way, you can define only properties that each file specifically uses.
### 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.
### Types for `data.extend()`
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 strict type checking, you can use one of the following methods:
```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 used 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])
```
## Using multiple loading 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, FeatureFlags } from "factorio:common"
declare const data: PrototypeData
declare const mods: ActiveMods
declare const feature_flags: FeatureFlags
// 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, FeatureFlags } from "factorio:common"
declare const data: SettingsData
declare const mods: ActiveMods
declare const feature_flags: FeatureFlags
```
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 on their corresponding event data type and filters, which is used by various methods in `script`.
You can pass a 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
// script.on_event("my-customm-input", () => {}) // mispelled, will error
```
The type `CustomInputName` (not plural) will be a union of strings, for all custom input names.
If not specified like above, `CustomInputName` defaults to just `string`.
### Array-like classes
A few classes that have an index operator, a length operator, and have an array-like structure, which will 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 a type suffixed with `Array` (e.g. `MapPositionArray`) defines the array form.
Concepts in a "read" position are in table form. Concepts in a "write" position may be in either 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 (e.g. LuaEntity.recipe only works if this is crafting-machine). For these classes, there are more specific types you can _optionally_ use:
- A "Base" type (e.g. `BaseEntity`) which contains only members usable by all subclasses
- 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>