Branded types use TypeScript’s intersection-with-symbol pattern so plain string values cannot flow into APIs expecting different identifier kinds. At runtime values are still strings; branding is erased.
Use the exported constructors (PartyId(...), ContractId(...), …) at boundaries when you receive untrusted strings (HTTP JSON, env vars) before passing into helpers expecting branded types.
import {
PartyId,
ContractId,
TemplateId,
type DomainId,
type CommandId,
} from '@fairmint/canton-node-sdk';
Example
import { PartyId, ContractId, TemplateId } from '@fairmint/canton-node-sdk';
const owner = PartyId('OWN_PARTY_ID');
const offer = ContractId('CONTRACT_ID_FROM_ACS');
const rules = TemplateId('#splice-amulet:Splice.AmuletRules:AmuletRules');
async function exercise(
party: typeof owner,
cid: typeof offer,
tpl: typeof rules,
): Promise<void> {
console.log({ party, cid, tpl });
}
await exercise(owner, offer, rules);
Passing a raw string where a PartyId is expected fails type-checking unless cast or constructed.
Primary identifiers (focus)
| Type | Meaning |
|---|---|
PartyId | Party string (party::namespace style depending on deployment). |
ContractId | Ledger contract identifier returned by ACS, exercises, or APIs. |
TemplateId | Fully-qualified template string (#hash:Module:Entity or package-qualified form used by your network). |
Other exported brands include DomainId, CommandId, TransactionId, UpdateId, UserId, PackageId, IdentityProviderId, and WorkflowId — same zero-cost pattern.
Constructors
Each brand has a matching function Name(value: string): Name that performs an as cast. No validation runs — incorrect strings fail later on the participant.
Pitfalls
- No runtime validation: Branding does not replace ledger-side checks.
- Interop: JSON payloads use plain strings; convert at the edge once.
See also
createParty— returnsPartyId/ContractId.- BaseClient —
getPartyId()typed asPartyId.
Source
src/core/branded-types.ts on GitHub.