Why not always explicitly return null?

No, it's generally ​not​ a good practice to always explicitly return nullinstead of letting functions return undefinedimplicitly in TypeScript. Here's why:

Problems with Always Returning null

1. ​Inconsistent with JavaScript Conventions

// Against JS/TS conventions
function findUser(id: number): User | null {
  const user = users.get(id);
  if (!user) return null; // ❌ Forces null when undefined is conventional
  return user;
}

// More conventional
function findUser(id: number): User | undefined {
  return users.get(id); // ✅ Returns undefined naturally
}

2. ​Fights Language Semantics

// Unnecessarily verbose
function processValue(value: string): string | null {
  if (!value) return null;
  return value.trim(); // ❌ Converts empty string to null
}

// More natural
function processValue(value: string): string | undefined {
  if (!value) return undefined;
  return value.trim();
}

When to Use Each

Use undefinedfor:

  • Missing/Optional Values

  • Function Parameters​ (not provided)

  • Object Properties​ (not defined)

  • Array Elements​ (not present)

// Natural undefined usage
interface Config {
  timeout?: number; // Naturally undefined if not provided
}

const getUser = (id: number): User | undefined => {
  return database.users[id]; // Naturally undefined if not found
};

Use nullfor:

  • Explicit Empty/Null States

  • Database NULL Values

  • Intentional Absence

// Appropriate null usage
interface User {
  name: string;
  deletedAt: Date | null; // Explicit "soft delete" state
}

function fetchData(): Promise<Data | null> {
  if (offlineMode) return null; // Explicit "unavailable" state
}

Better Practice: Be Consistent

Option 1: Prefer undefined(Recommended)

// Consistent undefined approach
class ApiService {
  private cache = new Map<string, Data>();
  
  getData(key: string): Data | undefined {
    return this.cache.get(key); // Natural undefined
  }
  
  findUser(email: string): User | undefined {
    return users.find(u => u.email === email); // Natural undefined
  }
}

Option 2: Prefer null(If You Must)

// If you choose null, be consistent everywhere
class ApiService {
  private cache = new Map<string, Data>();
  
  getData(key: string): Data | null {
    return this.cache.get(key) ?? null; // Convert undefined to null
  }
  
  findUser(email: string): User | null {
    return users.find(u => u.email === email) ?? null;
  }
}

TypeScript Benefits of Using undefined

1. ​Better Type Inference

// TypeScript understands optional/undefined naturally
interface User {
  id: number;
  avatar?: string; // Implicitly string | undefined
}

const user: User = { id: 1 };
console.log(user.avatar); // Type: string | undefined

2. ​Optional Chaining Works Naturally

// Works seamlessly with undefined
const userName = user?.profile?.name; // Type: string | undefined

// Versus null (requires conversion)
const userName = user?.profile?.name ?? null; // Extra step needed

3. ​Default Parameters

// undefined triggers default values naturally
function greet(name: string = "Anonymous") {
  return `Hello, ${name}!`;
}

greet(undefined); // "Hello, Anonymous!"
greet(null);     // "Hello, null!" 😕

When to Definitely Use null

// Use null for explicit "empty" states
interface FormState {
  values: Record<string, string>;
  errors: Record<string, string> | null; // null = not yet validated
  submittedAt: Date | null; // null = never submitted
}

// Use null for database NULL values
interface Product {
  id: number;
  categoryId: number | null; // NULL in database = no category
}

Recommendation

Prefer undefined​ for most cases because:

  1. It's the language's natural "missing value"

  2. Better tooling support

  3. More predictable behavior

  4. Less boilerplate code

Use nullsparingly​ for explicit "empty state" semantics where the distinction from "not set" matters.

The worst practice is mixing them inconsistently. Choose one approach and stick with it throughout your codebase.

char* filename_completion_function(const char* text, int state) { static DIR* dir = NULL; static char *filename = NULL, *dirname = NULL; static size_t filename_len = 0; struct dirent* entry; char* temp; size_t len; // 状态重置:当state==0时,表示新的补全请求 if (state == 0) { // 释放之前资源(增加空指针检查) if (dir) { closedir(dir); dir = NULL; } if (filename) { XFREE(MTYPE_TMP, filename); filename = NULL; } if (dirname) { XFREE(MTYPE_TMP, dirname); dirname = NULL; } // 解析text,分离目录和文件名部分 const char *temp_ptr = strrchr(text, '/'); if (temp_ptr) { // 提取文件名部分(最后一个斜杠之后的内容) filename = XSTRDUP(MTYPE_TMP, temp_ptr + 1); if (!filename) return NULL; // 提取目录部分(包括最后一个斜杠) len = temp_ptr - text + 1; // 包括斜杠 dirname = XMALLOC(MTYPE_TMP, len + 1); if (!dirname) { XFREE(MTYPE_TMP, filename); return NULL; } strncpy(dirname, text, len); dirname[len] = '\0'; // 确保终止符 } else { // 没有斜杠,说明只有文件名(保持无前缀) filename = XSTRDUP(MTYPE_TMP, text); if (!filename) return NULL; dirname = XSTRDUP(MTYPE_TMP, ""); // 空字符串代替"./" if (!dirname) { XFREE(MTYPE_TMP, filename); return NULL; } } // 处理家目录展开(~user) if (dirname[0] == '~') { char *expanded = tilde_expand(dirname); if (expanded) { XFREE(MTYPE_TMP, dirname); dirname = expanded; } } filename_len = filename ? strlen(filename) : 0; // 打开目录(处理空目录名情况) const char *open_path = (dirname[0] == '\0') ? "." : dirname; dir = opendir(open_path); if (!dir) { XFREE(MTYPE_TMP, filename); XFREE(MTYPE_TMP, dirname); filename = NULL; dirname = NULL; return NULL; } } // 如果目录流未打开或资源未初始化,返回NULL if (!dir || !filename || !dirname) { return NULL; } // 遍历目录,寻找匹配项 while ((entry = readdir(dir)) != NULL) { // 跳过隐藏文件(以.开头),除非文件名以.开头 if (filename[0] != '.' && entry->d_name[0] == '.') { // 但允许匹配显式指定的隐藏文件 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } } // 检查文件名是否匹配 size_t entry_len = strlen(entry->d_name); if (entry_len < filename_len) { continue; } if (strncmp(entry->d_name, filename, filename_len) == 0) { // 计算路径长度(避免空目录名问题) size_t dir_len = strlen(dirname); len = dir_len + entry_len + 2; // +1 for null, +1 for possible '/' temp = XMALLOC(MTYPE_TMP, len); if (!temp) continue; // 分配失败则跳过 // 拼接目录和文件名(处理空目录名) if (dir_len > 0) { snprintf(temp, len, "%s%s", dirname, entry->d_name); } else { strncpy(temp, entry->d_name, len); } // 检查是否为目录(使用d_type优化性能) int is_dir = 0; #if defined(_DIRENT_HAVE_D_TYPE) || defined(__linux__) if (entry->d_type == DT_DIR) { is_dir = 1; } else if (entry->d_type == DT_LNK || entry->d_type == DT_UNKNOWN) { // 需要stat检查 struct stat st; if (stat(temp, &st) == 0 && S_ISDIR(st.st_mode)) { is_dir = 1; } } #else // 没有d_type支持的系统使用stat struct stat st; if (stat(temp, &st) == 0 && S_ISDIR(st.st_mode)) { is_dir = 1; } #endif // 如果是目录,添加斜杠 if (is_dir) { size_t cur_len = strlen(temp); if (cur_len + 1 < len) { temp[cur_len] = '/'; temp[cur_len + 1] = '\0'; } else { // 重新分配以容纳斜杠 char *new_temp = XREALLOC(MTYPE_TMP, temp, cur_len + 2); if (new_temp) { temp = new_temp; strcat(temp, "/"); } } } // 返回当前匹配项 return temp; } } // 没有更多匹配项,清理资源 closedir(dir); dir = NULL; XFREE(MTYPE_TMP, filename); XFREE(MTYPE_TMP, dirname); filename = NULL; dirname = NULL; return NULL; } 我还是使用的这个函数 上层还是: char** cmlsh_completion(char* text, int start, int end) { char** matches; pal_printf("\ncmlsh_completion: enter------------------\n"); matches = completion_matches(text, cmlsh_completion_matches); // 如果命令补全失败,再尝试路径补全 if (!matches || !matches[0]) { matches = filename_completion_matches(text, filename_completion_function); } return matches; } 最上层是: void cmlsh_readline_init() { /* Input mode set to RAW mode. */ readline_zebos_set_excl_raw_input_mode(); /* '?' is the description function. */ rl_bind_key('?', cmlsh_describe); rl_bind_key('\t', rl_complete); /* Register completion function. */ rl_attempted_completion_function = (CPPFunction*)cmlsh_completion; } 但是这个代码逻辑只能显示文件夹,不能显示当前路径下的普通文件 例如: 假设当前etc目录下有1.txt 2.txt abc子目录,然后我输入dir e再按tab键补全,想要的效果是能显示出dir etc/ , 然后我再按tab键进行联想,应该要显示的是dir etc/1.txt ,然后我再按tab键进行联想,应该要显示的是dir etc/2.txt , 然后我再按tab键进行联想,应该要显示的是dir etc/abc/,然后我再按tab键进行联想,应该要显示的是dir etc/ 这样循环显示,每次只显示一次
07-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值