- TypeScript
TypeScript try-catchのerrorはunknown 型の扱い方
TypeScript の catch で受け取る変数は unknown 型です。そのため e.message と書くと TS18046 になり、instanceof Error などで型を絞り込んでから使います。 JavaScript では Error 以外の値(文字列や null)も throw できるので、「何が入っているかわからない」という型になっています。
この記事では、TS18046 の出力全文と、instanceof Error・自作の型ガード・toError 変換関数の3つの直し方を、7種類の値を throw した実行結果で比べます。あわせて、カスタムエラーと Error.cause、Error.isError、Promise でのエラーの型、判別可能ユニオンで作る Result 型のコードと、「例外を投げる/Result を返す」の使い分けを表にまとめました。
確認環境:Ubuntu 24.04 / TypeScript 7.0.2(一部の比較に 5.9.3)/ Node.js 22.22.2 と 24.21.0 / @types/node 22.20.5。TypeScript のファイルは node ファイル名.ts でそのまま実行しています。
catchのerrorの扱い方の早見表
| やりたいこと | 書き方 | ポイント |
|---|---|---|
| message を読む | if (e instanceof Error) { e.message } |
いちばん短い。Error 以外は else で扱う |
| message を持つオブジェクトも読む | 自作の型ガード hasMessage(e) |
Error でないオブジェクトの throw にも対応 |
| 何が来ても Error にそろえる | const err = toError(e) |
文字列や null も Error に変換して先へ渡す |
| エラーの種類で分ける | class NotFoundError extends Error と instanceof |
name と独自のプロパティを持たせる |
| 元のエラーを残して包み直す | new Error("…", { cause: e }) |
cause の型は unknown |
| 別の実行環境の Error も見分ける | Error.isError(e) |
Node.js 24 で動作。lib は esnext |
| エラーを戻り値で表す | Result<T, E>(判別可能ユニオン) |
起こりうるエラーが型に現れる |
| 場面 | エラーの値の型(tsc 7.0.2) |
|---|---|
catch (e) |
unknown |
promise.catch((e) => …) の e |
any |
Promise.allSettled の reason |
any |
error.cause |
unknown |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「TypeScript try-catchのerrorはunknown 型の扱い方」
e.messageでTS18046が出る理由とエラー全文
try {
JSON.parse("{");
} catch (e) {
console.log(e.message);
}
try {
JSON.parse("{");
} catch (e: Error) {
console.log(e.message);
}
Promise.reject(new Error("x")).catch((e) => {
console.log(e.message);
});
$ npx tsc -p .
src/e1.ts(4,15): error TS18046: 'e' is of type 'unknown'.
src/e1.ts(9,13): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.
--locale ja を付けたときの TS1196 は次のとおりです。
src/e1.ts(9,13): error TS1196: Catch 句の変数型の注釈を指定する場合は、'any' または 'unknown' にする必要があります。
| 番号 | 意味 | 直し方 |
|---|---|---|
| TS18046 | e が unknown のままプロパティを読んだ | 絞り込んでから読む(次の章) |
| TS1196 | catch の変数に Error などの型を書いた | 注釈は書かないか、unknown にする |
- catch の変数が unknown になるのは、strict に含まれる useUnknownInCatchVariables の働きです。
npx tsc -p . --useUnknownInCatchVariables falseで試すと、TS18046 は消えて TS1196 だけが残りました。このときの e は any です。 catch (e: Error)とは書けません。 throw される値を tsc は追えないので、書けるのは any か unknown だけです。- 13〜15行目の
.catch((e) => …)はエラーになりません。 こちらの e は any だからです(あとの章で扱います)。
unknownを絞り込む3つの直し方を比べる
3つの直し方を関数にして、7種類の値を throw した場合の結果を比べます。
// 直し方1:instanceof Error
function byInstanceof(e: unknown): string {
if (e instanceof Error) {
return e.message;
}
return "(Error ではない)";
}
// 直し方2:自作の型ガード(message を持つオブジェクトなら読む)
function hasMessage(e: unknown): e is { message: string } {
return typeof e === "object" && e !== null && "message" in e && typeof e.message === "string";
}
function byGuard(e: unknown): string {
if (hasMessage(e)) {
return e.message;
}
return "(message がない)";
}
// 直し方3:何が来ても Error に変換する
function toError(e: unknown): Error {
if (e instanceof Error) return e;
if (typeof e === "string") return new Error(e);
if (hasMessage(e)) return new Error(e.message, { cause: e });
return new Error(`Error 以外が throw されました: ${String(e)}`, { cause: e });
}
function byToError(e: unknown): string {
return toError(e).message;
}
const cases: [string, () => never][] = [
["new Error", () => { throw new Error("在庫がありません"); }],
["TypeError", () => { throw new TypeError("数値ではありません"); }],
["文字列", () => { throw "在庫がありません"; }],
["オブジェクト", () => { throw { code: "E001", message: "在庫がありません" }; }],
["null", () => { throw null; }],
["undefined", () => { throw undefined; }],
["数値", () => { throw 404; }],
];
console.log(`Node.js ${process.version}`);
for (const [label, run] of cases) {
try {
run();
} catch (e) {
console.log(`■ ${label}`);
console.log(` 1.instanceof: ${byInstanceof(e)}`);
console.log(` 2.型ガード : ${byGuard(e)}`);
console.log(` 3.toError : ${byToError(e)}`);
}
}
$ node src/compare.ts
Node.js v22.22.2
■ new Error
1.instanceof: 在庫がありません
2.型ガード : 在庫がありません
3.toError : 在庫がありません
■ TypeError
1.instanceof: 数値ではありません
2.型ガード : 数値ではありません
3.toError : 数値ではありません
■ 文字列
1.instanceof: (Error ではない)
2.型ガード : (message がない)
3.toError : 在庫がありません
■ オブジェクト
1.instanceof: (Error ではない)
2.型ガード : 在庫がありません
3.toError : 在庫がありません
■ null
1.instanceof: (Error ではない)
2.型ガード : (message がない)
3.toError : Error 以外が throw されました: null
■ undefined
1.instanceof: (Error ではない)
2.型ガード : (message がない)
3.toError : Error 以外が throw されました: undefined
■ 数値
1.instanceof: (Error ではない)
2.型ガード : (message がない)
3.toError : Error 以外が throw されました: 404
Node.js 24.21.0 でも、1行目のバージョン表示以外は同じ出力でした。
| throw した値 | 1. instanceof Error | 2. 型ガード | 3. toError |
|---|---|---|---|
| Error、TypeError | message を読める | message を読める | message を読める |
| 文字列 | 読めない(else 側) | 読めない(else 側) | 文字列が message になる |
| message を持つオブジェクト | 読めない(else 側) | message を読める | message を読める |
| null、undefined、数値 | 読めない(else 側) | 読めない(else 側) | 何が来たかを message に入れる |
| 直し方 | 向いている場面 |
|---|---|
| 1. instanceof Error | 自分のコードと標準の機能だけが throw する範囲。まずはこれで十分 |
| 2. 型ガード | Error を継承していないオブジェクトを throw するライブラリと組み合わせるとき |
| 3. toError | ログ出力や画面表示など、「必ず Error として扱いたい」場所の手前 |
toError は、元の値を cause に入れて残している点がポイントです。変換後も、もとが何だったかを調べられます。なお、型ガードの e is { message: string } は、関数の中身が正しいことを前提に tsc が信じる書き方なので、判定の式は丁寧に書きます。
カスタムエラーとError.causeで種類と原因を表す
エラーの種類で処理を分けたいときは、Error を継承したクラスを作り、instanceof で絞り込みます。
class AppError extends Error {
override name = "AppError";
readonly code: string;
constructor(message: string, code: string, options?: ErrorOptions) {
super(message, options);
this.code = code;
}
}
class NotFoundError extends AppError {
override name = "NotFoundError";
readonly resource: string;
constructor(resource: string, options?: ErrorOptions) {
super(`${resource} が見つかりません`, "NOT_FOUND", options);
this.resource = resource;
}
}
function loadConfig(path: string): string {
try {
JSON.parse("{ broken");
return path;
} catch (e) {
// 元のエラーを cause に入れて、意味のわかるエラーに包み直す
throw new NotFoundError(`設定ファイル ${path}`, { cause: e });
}
}
try {
loadConfig("app.json");
} catch (e) {
console.log("instanceof Error :", e instanceof Error);
console.log("instanceof AppError :", e instanceof AppError);
console.log("instanceof NotFoundError:", e instanceof NotFoundError);
if (e instanceof NotFoundError) {
console.log("name :", e.name);
console.log("message :", e.message);
console.log("code :", e.code);
console.log("resource:", e.resource);
console.log("cause :", e.cause instanceof Error ? `${e.cause.name}: ${e.cause.message}` : e.cause);
}
console.log(String(e));
}
$ node src/custom.ts
instanceof Error : true
instanceof AppError : true
instanceof NotFoundError: true
name : NotFoundError
message : 設定ファイル app.json が見つかりません
code : NOT_FOUND
resource: 設定ファイル app.json
cause : SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)
NotFoundError: 設定ファイル app.json が見つかりません
Node.js 22.22.2 と 24.21.0 で同じ出力でした。
override name = "…"を書いておくと、String(e)やログの先頭がクラス名になります。書かないと、継承していてもError: …と表示されます。- instanceof で絞ると、code や resource といった独自のプロパティが型つきで読めます。
e.causeの型は unknown なので、cause を読むときも instanceof で絞ります。- 2番目の引数の型
ErrorOptionsは、tsconfig.json の lib が es2022 以降のときに使えます。lib を es2021 にすると、次のエラーになりました。
es21/a.ts(1,32): error TS2554: Expected 0-1 arguments, but got 2.
es21/a.ts(2,15): error TS2550: Property 'cause' does not exist on type 'Error'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2022' or later.
コンストラクタの引数に readonly code: string と書く省略記法(パラメータプロパティ)は、Node.js で .ts をそのまま実行すると ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX で止まったため、上のコードではプロパティを別に宣言しています。
targetがES5のときinstanceofがfalseになる件
「Error を継承したクラスで instanceof が false になる」という話は、出力先(target)が ES5 のときに起きます。次のコードを TypeScript 5.9.3 で target を変えてコンパイルし、Node.js 22.22.2 で実行しました。
class AppError extends Error {
constructor(message: string) {
super(message);
this.name = "AppError";
}
describe() { return "AppError: " + this.message; }
}
class FixedError extends Error {
constructor(message: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype); // プロトタイプを付け直す
this.name = "FixedError";
}
describe() { return "FixedError: " + this.message; }
}
for (const E of [AppError, FixedError]) {
try {
throw new E("在庫がありません");
} catch (e) {
console.log(E === AppError ? "AppError" : "FixedError");
console.log(" instanceof Error:", e instanceof Error);
console.log(" instanceof 自分 :", e instanceof E);
console.log(" describe の型 :", typeof (e as any).describe);
}
}
== TS 5.9.3 --target es5
AppError
instanceof Error: true
instanceof 自分 : false
describe の型 : undefined
FixedError
instanceof Error: true
instanceof 自分 : true
describe の型 : function
== TS 5.9.3 --target es2015
AppError
instanceof Error: true
instanceof 自分 : true
describe の型 : function
FixedError
instanceof Error: true
instanceof 自分 : true
describe の型 : function
| コンパイラと target | e instanceof AppError |
メソッド describe |
|---|---|---|
| TypeScript 5.9.3、es5 | false | 呼べない(undefined) |
| TypeScript 5.9.3、es5、setPrototypeOf あり | true | 呼べる |
| TypeScript 5.9.3、es2015 / es2022 | true | 呼べる |
| TypeScript 7.0.2、es2015 | true | 呼べる |
| TypeScript 7.0.2、es5 | コンパイルできない(TS5108) | ― |
target が es2015 以降なら、対策なしで instanceof は true です。TypeScript 7.0.2 では target の es5 そのものが指定できず、次のエラーになりました。
error TS5108: Option 'target=ES5' has been removed. Please remove it from your configuration.
以前の版で target を es5 にしている場合だけ、コンストラクタに Object.setPrototypeOf(this, new.target.prototype) を入れます。
Error.isErrorで別の実行環境のErrorも見分ける
instanceof は「同じ実行環境の Error から作られたか」を見るため、node:vm で作った別のコンテキストのように、別の環境で作られた Error には false を返します。Error.isError は、作られた場所に関係なく Error かどうかを判定します。
import vm from "node:vm";
// 別の実行環境(別の Realm)で作られた Error
const foreign: unknown = vm.runInNewContext("new Error('別の環境のエラー')");
console.log("instanceof Error:", foreign instanceof Error);
console.log("Error.isError :", Error.isError(foreign));
console.log("Error.isError({ message: 'x' }):", Error.isError({ message: "x" }));
if (Error.isError(foreign)) {
console.log(foreign.message);
}
$ node src/iserror.ts (Node.js 24.21.0)
instanceof Error: false
Error.isError : true
Error.isError({ message: 'x' }): false
別の環境のエラー
Error.isError(foreign) の if の中では、foreign が Error に絞り込まれ、message を読めています。使うときの条件は2つあります。
| 確認したこと | 結果 |
|---|---|
| tsconfig.json の lib が es2022 | TS2550: Property 'isError' does not exist on type 'ErrorConstructor'. …Try changing the 'lib' compiler option to 'esnext' or later. |
| lib を esnext に変更 | tsc が通る |
| Node.js 22.22.2 で実行 | TypeError: Error.isError is not a function |
| Node.js 24.21.0 で実行 | 上の出力のとおり動く |
同じ環境の中だけで完結するコードなら、instanceof Error で足ります。
Promiseのrejectとawaitでのエラーの型
非同期の処理では、書き方によってエラーの値の型が変わります。
| 書き方 | エラーの値の型 | 対応 |
|---|---|---|
try { await p } catch (e) { } |
unknown |
同期のときと同じく絞り込む |
p.catch((e) => { }) |
any |
(e: unknown) と自分で書く |
Promise.allSettled の r.reason |
any |
const reason: unknown = r.reason で受け直す |
any のままだと、e.mesage のようなつづりの間違いも tsc を通ります。unknown と書いておくと、同期の catch と同じ絞り込みがそのまま使えます。
async function fetchStock(id: number): Promise<number> {
if (id < 0) throw new RangeError("id は 0 以上で指定します");
if (id === 0) return Promise.reject("not found"); // 文字列で reject する例
return 10;
}
// 1. await + try...catch:e は unknown
try {
await fetchStock(-1);
} catch (e) {
console.log("1:", e instanceof RangeError ? e.message : e);
}
// 2. .catch():コールバックの引数は any(自分で unknown と書く)
await fetchStock(0).catch((e: unknown) => {
console.log("2:", e instanceof Error ? e.message : `Error 以外: ${String(e)}`);
});
// 3. Promise.allSettled:reason は any
const results = await Promise.allSettled([fetchStock(1), fetchStock(-1), fetchStock(0)]);
for (const r of results) {
if (r.status === "fulfilled") {
console.log("3: 成功", r.value);
} else {
const reason: unknown = r.reason;
console.log("3: エラー", reason instanceof Error ? reason.message : reason);
}
}
// 4. await を付けないと try...catch では受け取れない
function withoutAwait(): Promise<number> {
try {
return fetchStock(-1);
} catch (e) {
console.log("4: ここには来ない");
return Promise.resolve(0);
}
}
async function withAwait(): Promise<number> {
try {
return await fetchStock(-1);
} catch (e) {
console.log("4: await ありなら catch に来る");
return 0;
}
}
await withoutAwait().catch(() => console.log("4: await なしは呼び出し元に届く"));
await withAwait();
$ node src/async.ts
1: id は 0 以上で指定します
2: Error 以外: not found
3: 成功 10
3: エラー id は 0 以上で指定します
3: エラー not found
4: await なしは呼び出し元に届く
4: await ありなら catch に来る
- reject の理由にも、Error 以外の値を渡せます。 2 と 3 の
not foundは文字列です。自分で reject するときはnew Error(…)を渡すと、受け取る側が instanceof で扱えます。 - 4 のように、try の中で Promise を await せずに return すると、catch には来ません。 拒否された Promise がそのまま呼び出し元へ渡ります。try...catch で受けたいときは
return awaitと書きます。
判別可能ユニオンで作るResult型
「在庫が足りない」「数量が正しくない」のように、起こることがあらかじめわかっているエラーは、戻り値の型で表す方法があります。成功と、そうでない場合を ok というプロパティで見分ける形(判別可能ユニオン)にします。
export type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
export const ok = <T>(value: T): Result<T, never> => ({ ok: true, value });
export const err = <E>(error: E): Result<never, E> => ({ ok: false, error });
/** 例外を投げる関数を Result に変える */
export function tryCatch<T>(fn: () => T): Result<T> {
try {
return ok(fn());
} catch (e) {
return err(e instanceof Error ? e : new Error(String(e), { cause: e }));
}
}
/** Promise を Result に変える */
export async function tryAsync<T>(promise: Promise<T>): Promise<Result<T>> {
try {
return ok(await promise);
} catch (e) {
return err(e instanceof Error ? e : new Error(String(e), { cause: e }));
}
}
// ---- 使う側 ----
type OrderError =
| { kind: "out_of_stock"; item: string; stock: number }
| { kind: "invalid_quantity"; quantity: number };
const stock: Record<string, number> = { pen: 3, note: 0 };
function order(item: string, quantity: number): Result<{ item: string; rest: number }, OrderError> {
if (!Number.isInteger(quantity) || quantity <= 0) {
return err({ kind: "invalid_quantity", quantity });
}
const current = stock[item] ?? 0;
if (current < quantity) {
return err({ kind: "out_of_stock", item, stock: current });
}
return ok({ item, rest: current - quantity });
}
function describe(e: OrderError): string {
switch (e.kind) {
case "out_of_stock":
return `${e.item} は在庫が ${e.stock} 個です`;
case "invalid_quantity":
return `数量 ${e.quantity} は指定できません`;
default: {
const rest: never = e; // kind を増やして case を書き忘れると、ここが型エラーになる
return rest;
}
}
}
for (const [item, quantity] of [["pen", 2], ["note", 1], ["pen", 0]] as const) {
const r = order(item, quantity);
if (r.ok) {
console.log(`注文できました: ${r.value.item} 残り ${r.value.rest}`);
} else {
console.log(`注文できません: ${describe(r.error)}`);
}
}
const parsed = tryCatch(() => JSON.parse("{ broken") as unknown);
console.log(parsed.ok ? parsed.value : `tryCatch: ${parsed.error.name}`);
const waited = await tryAsync(Promise.reject("timeout"));
console.log(waited.ok ? waited.value : `tryAsync: ${waited.error.message}`);
$ node src/result.ts
注文できました: pen 残り 1
注文できません: note は在庫が 0 個です
注文できません: 数量 0 は指定できません
tryCatch: SyntaxError
tryAsync: timeout
Result の良さは、確認を忘れると tsc が止めてくれることです。ok を確かめずに value を読むと、次のエラーになります。
import { type Result, ok } from "./result.ts";
declare function order(): Result<{ rest: number }, { kind: "out_of_stock" }>;
const r = order();
console.log(r.value.rest);
src/result-err.ts(4,15): error TS2339: Property 'value' does not exist on type 'Result<{ rest: number; }, { kind: "out_of_stock"; }>'.
Property 'value' does not exist on type '{ ok: false; error: { kind: "out_of_stock"; }; }'.
- error の型を kind つきのユニオンにすると、switch で種類ごとに必要な情報(在庫数、指定された数量)を型つきで取り出せます。
- describe の default にある never への代入は、kind を増やしたのに case を書き足していない場合に型エラーで知らせる仕組みです。
- tryCatch と tryAsync は、例外を投げる既存の関数や Promise を Result に変える橋渡しです。JSON.parse の SyntaxError も、文字列での reject も、Error を持つ Result になりました。
例外を投げるかResultを返すかの使い分け
| 観点 | 例外を投げる(throw) | Result を返す |
|---|---|---|
| エラーの型 | catch で unknown。型には現れない | 関数の戻り値の型に現れる |
| 確認を忘れたとき | 実行時に上位まで伝わる | tsc が TS2339 で知らせる |
| 途中の関数 | 何も書かなくても上へ伝わる | 各段で受けて返す必要がある |
| 向いているエラー | 想定していない不具合、続行できない状態(設定の読み込みに必要なファイルがない、引数の使い方の誤り) | 起こることがわかっていて、呼び出し側が対応を決めるもの(入力の検証、在庫が足りない、該当なし) |
| 標準の機能・ライブラリとの相性 | そのまま合う | tryCatch などで変換して受ける |
| 状況 | 選び方 |
|---|---|
| 呼び出し側が、その場で分岐して画面やレスポンスを変える | Result |
| エラーの種類ごとに渡したい情報が違う | Result(kind つきのユニオン) |
| どの層でも対処できず、いちばん外側でまとめて記録する | 例外 |
| 標準の機能が投げる例外を受ける境目 | try...catch で受け、toError か tryCatch で形をそろえる |
「想定している出来事は Result、想定していない不具合は例外」と分けると、Result の error には利用者に伝える意味のある種類だけが並び、例外は調査が必要なものだけになります。両方を1つのコードの中で使い分けて構いません。
この記事の tsc の出力は、次の tsconfig.json で取りました。
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"lib": ["esnext"],
"types": ["node"],
"strict": true,
"rootDir": "src",
"noEmit": true,
"allowImportingTsExtensions": true
},
"include": ["src"]
}
"lib": ["esnext"] は Error.isError のためで、それ以外のコードは "es2022" で通ります。"allowImportingTsExtensions" は、import に .ts の拡張子を書くための設定です。
よくある質問
Q. catch (e: any) と書いてもよいですか?
A. 書けますが、e.message のつづりを間違えても tsc が気づけなくなります。unknown のまま instanceof Error で絞るほうが、1行増えるだけで安全に読めます。
Q. (e as Error).message ではいけませんか?
A. tsc は通ります。ただし文字列や null が throw された場合、as は何も調べないので、message は undefined になるか、null のプロパティを読んで TypeError になります。instanceof なら、Error でない場合の分岐も一緒に書けます。
Q. Result 型はライブラリを使うべきですか? A. この記事の Result、ok、err、tryCatch、tryAsync だけなら 30 行ほどなので、まずは自作で十分です。map などの変換をつなげて書きたくなったら、専用のライブラリを検討する段階です。
Q. カスタムエラーと Result はどちらを使えばよいですか? A. 併用できます。上位まで伝えて止めたいものはカスタムエラーを throw し、呼び出し側に分岐してほしいものは Result で返します。Result の error にカスタムエラーのインスタンスを入れる形も取れます。
まとめ
- catch の変数は unknown。
e.messageをそのまま読むと TS18046、catch (e: Error)は TS1196 になる - 直し方は instanceof Error・型ガード・toError の3つ。文字列や null が throw されても扱えるのは toError
- カスタムエラーは
override nameを書き、instanceof で絞ると独自のプロパティを型つきで読める new Error("…", { cause: e })で元のエラーを残す。cause の型も unknown- instanceof が false になるのは target が es5 のとき(TypeScript 5.9.3 で確認)。es2015 以降は true
.catch((e) => …)と allSettled の reason は any。自分で unknown と書いて同じ絞り込みを使う- 想定している出来事は Result、想定していない不具合は例外。Result は確認を忘れると tsc が知らせてくれる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「TypeScript try-catchのerrorはunknown 型の扱い方」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。