# Definition (DefinitionBase & TsDefinition)



## Source [#source]

* `skmtc/deno/core/dsl/Definition.ts` — `DefinitionBase` (abstract)
* `skmtc/deno/lang-typescript/src/TsDefinition.ts` — `TsDefinition`

(Core declares only the abstract `DefinitionBase`; the concrete
rendering class is the lang package's `TsDefinition`.)

## DefinitionBase (`@skmtc/core`) — the coordination surface [#definitionbase-skmtccore--the-coordination-surface]

```ts
abstract class DefinitionBase<V extends GeneratedValue = GeneratedValue> extends SnippetBase {
  identifier: Identifier
  value: V

  constructor(args: { context: GenerateContextType; identifier: Identifier; value: V })

  abstract toString(): string
}
```

The cross-generator cache reads only this surface — the definition's
`identifier` (the `(name, exportPath)` cache key), its `value`, and
the `generatorKey` (via `SnippetBase`) for the integrity check. How a
definition renders — the `export const X = ...` wrapper, JSDoc, the
visibility keyword — is the concrete language subclass's concern.

Engine code types against the base: `findDefinition` returns
`DefinitionBase | undefined`; `context.register({ definitions })`
accepts `DefinitionBase[]`; `insertNormalizedModel` returns
`DefinitionBase<V>`; `GeneratedDefinition<V> = DefinitionBase<V>`.

## TsDefinition (`@skmtc/lang-typescript`) — the TypeScript renderer [#tsdefinition-skmtclang-typescript--the-typescript-renderer]

```ts
class TsDefinition<Value extends GeneratedValue = GeneratedValue> extends DefinitionBase<Value> {
  description: string | undefined
  noExport: boolean | undefined

  constructor(args: {
    context: GenerateContextType
    identifier: Identifier
    value: Value
    description?: string   // optional JSDoc text
    noExport?: boolean     // omit the `export` keyword
  })

  override toString(): string
}
```

`toString()` assembles the declaration:

1. Optional JSDoc block (from `description`, via `withDescription`)
2. `export` keyword (unless `noExport`)
3. Declaration keyword from the identifier's `kind`
   (`toTsKeyword`: `'variable'` → `const`, `'type'` → `type`)
4. Identifier name (with optional `: typeName` annotation)
5. `=`, the value (stringified via template interpolation), `;\n`

Output examples:

```ts
// kind 'variable', no typeName
export const userBody = z.object({ name: z.string() });

// kind 'variable' with typeName
export const useCreateUser: UseMutationResult<UserData, Error, CreateUserArgs> = (...) => { ... };

// kind 'type'
export type UserBody = { name: string; email: string };

// with description
/** The validated request body for creating a user. */
export const createUserBody = z.object({ ... });

// noExport (rare)
const _privateHelper = (...) => { ... };
```

## How Drivers create Definitions [#how-drivers-create-definitions]

Drivers never name a concrete class — they read the `Lang` off the
projection class's inherited static and use its factory:

```ts
// In ModelDriver / OasOperationDriver / GqlOperationDriver (simplified)
const cached = context.findDefinition({ name, exportPath })
if (cached && affirmDefinition(cached)) return cached

const value = new this.projection({ ... })          // construct the Projection
const definition = this.projection.lang.toDefinition({
  context, identifier, value, noExport
})                                                   // → a TsDefinition for TS generators

context.register({ definitions: [definition], destinationPath: exportPath })
```

When the file is later serialized, `definition.toString()` runs,
which interpolates `value` — the Projection's `toString()` — between
`export const NAME = ` and `;`.

## When to create a definition directly [#when-to-create-a-definition-directly]

Rare. For a one-off sibling declaration in a file you own (a
constants object, a default-values map), use the lang package's
`defineAndRegister` function — it builds the `TsDefinition` and
registers it in one step:

```ts fragment
import { defineAndRegister, createVariable } from '@skmtc/lang-typescript'

defineAndRegister(context, {
  identifier: createVariable('EMPTY_VALUES'),
  value: '{ ... }',                                  // raw string is fine
  destinationPath: this.settings.exportPath
})
```

**Trade-off**: bypasses cross-generator coordination — other
generators can't reach this definition via `insertOperation` /
`insertModel` (there is no Projection class to hand them). Use only
for definitions that don't need cross-generator discoverability; if a
peer might reference it by name, make it a Projection.

## Common questions [#common-questions]

### Is a definition really a Snippet? [#is-a-definition-really-a-snippet]

By inheritance, yes — `DefinitionBase extends SnippetBase` (which
provides `context` and the attribution surface). By role, no — it's
the bridging wrapper for Projections, addressed by file position
rather than embedded in templates.

### Can I update a definition after registering it? [#can-i-update-a-definition-after-registering-it]

No — definitions are append-only into the file map; first-write-wins
(`addDefinition` ignores a duplicate name). Vary content via
enrichments or the Projection's inputs, not by mutating a
registered definition. (One sanctioned exception: the accumulator
pattern mutates the *value* of a single shared definition — see
gen-msw for the worked example.)

### What happens if `value` has no `toString()`? [#what-happens-if-value-has-no-tostring]

You'd get `[object Object]` in the output — a clear bug signal. In
practice values are Projection instances (whose `toString()` contract
comes from `SnippetBase`) or strings.

## See also [#see-also]

* [API: Identifier](/docs/reference/api/dsl-identifier) — `identifier`, `kind`, `typeName`
* [API: GenerateContext](/docs/reference/api/generate-context) — `register({ definitions })`
* [API: SnippetBase](/docs/reference/api/dsl-snippet-base) — what DefinitionBase extends
* [Projections and Snippets concept](/docs/concepts/projections-and-snippets)
* [Cross-generator coordination concept](/docs/concepts/cross-generator-coordination)
* [Glossary: Definition](/docs/reference/glossary)
