UNPKG

typed-factorio

Version:

Featureful typescript definitions for the Factorio modding api.

224 lines (156 loc) 9.87 kB
[![NPM Version](https://img.shields.io/npm/v/typed-factorio)](https://www.npmjs.com/package/typed-factorio) ![Factorio version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.npmjs.com%2Ftyped-factorio%2Flatest&query=%24.factorioVersion&prefix=v&label=Factorio%20version) # 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>