Type safety
TaxKit uses TypeScript types and runtime schemas together. TypeScript checks the calculator descriptor and facts you pass at compile time. Runtime schema decoding still validates untrusted values before calculation.
Compile-time calculator inputs
The selected descriptor controls the accepted facts and the returned report:
import { Cents, aud } from "@taxkit/core/primitives";
import { TaxKit } from "@taxkit/sdk";
import { au } from "@taxkit/sdk/au";
const report = await TaxKit.calculate(au.calculations.annualIncomeTax, {
taxableIncome: aud(Cents.make(9_000_000)),
});
report.liability.cents;If you pass facts for another calculator, the call should fail type checking:
import { Cents, aud } from "@taxkit/core/primitives";
import { TaxKit } from "@taxkit/sdk";
import { au } from "@taxkit/sdk/au";
await TaxKit.calculate(au.calculations.annualIncomeTax, {
grossPay: aud(Cents.make(165_400)),
taxFreeThresholdClaimed: true,
});Branded values
Money values use canonical branded constructors from @taxkit/core:
import { Cents, aud } from "@taxkit/core/primitives";
const taxableIncome = aud(Cents.make(9_000_000));Use Cents.make for known constants and aud for cents you have already
checked. Use the fallible audFromCents helper when a number still needs
checking. See Money and branded values
for checked arithmetic and error handling.
Runtime schema decoding
Compile-time checks protect code you compile. Runtime schema decoding protects values that come from users, forms, JSON or other services.
Decode external JSON with the selected calculator's input schema before calling the typed SDK. In this example, annual tax facts fail the pay calculator's schema check; the valid weekly pay input reaches calculation.
import { au } from "@taxkit/sdk/au";
import { Effect, Schema } from "effect";
// Check an external JSON representation before calling a typed calculator.
export const invalidInput = Schema.decodeEffect(
Schema.fromJsonString(au.calculations.takeHomePay.inputSchema)
)('{"taxableIncome":{"_tag":"Money","cents":9000000,"currency":"AUD"}}');
export const validInput = Schema.decodeEffect(
Schema.fromJsonString(au.calculations.takeHomePay.inputSchema)
)(
'{"grossPay":{"_tag":"GrossPay","amount":{"_tag":"Money","cents":165400,"currency":"AUD"},"period":"weekly"},"taxFreeThresholdClaimed":true}'
).pipe(
Effect.flatMap((input) => Effect.promise(() => au.pay.takeHomePay(input)))
);These values are Effect programs for your application's runner.
invalidInput fails with SchemaError before the SDK is called. The complete
example is checked in the example source.
See Handle validation errors for
handling this error separately from calculation failures.
Full run typing
Effect consumers can preserve the full CalculatorRunResponse shape while
narrowing report to the selected descriptor output:
import type { SdkCalculatorRunResponse } from "@taxkit/sdk/effect";
import type { CalculatorRunResponse } from "@taxkit/sdk/schemas";SdkCalculatorRunResponse<Report> keeps the canonical response shape and
narrows report to the decoded report type.